Лучший способ разобрать входящий почтовый запрос HTTP на Arduino?

Я пишу простой веб-сервер HTTP на своем Arduino Uno Wifi Rev2. для обработки входящего запроса HTTP POST в формате JSON.

Вот как я отправляю HTTP-запрос (с JSON) от моего клиента:

curl \
--request POST \
--header "Content-Type: application/json" \
--data '{
    "A": "B",
    "C": "D"    
}' \
"http://192.168.4.1/myEndpoint"

Это строка, которую получает веб-сервер Arduino:

POST /myEndpoint HTTP/1.1\r\nHost: 192.168.4.1\r\nUser-Agent: curl/7.54.0\r\nAccept: */*\r\nContent-Type: application/json\r\nContent-Length: 34\r\n\r\n{\n    "A": "B",\n    "C": "D"    \n}

Я использую библиотеку Arduino Regexp Ника Гэммона, чтобы анализировать этот запрос, проверять его и извлекать данные JSON.

Это работает, но синтаксический анализ HTTP-запроса таким образом чрезвычайно хрупок и кажется хакерским. Это может легко сломаться, если другой клиент переупорядочивает/опускает заголовок или пропускает символы возврата каретки. Это ужасное регулярное выражение, которое я использую для проверки:

    httpRegexp = "POST /myEndpoint HTTP/[%d%.]+%\r%\nHost: 192%.168%.4%.1%\r%\nUser%-Agent: curl/[%d%.]+%\r%\nAccept: %*/%*%\r%\nContent%-Type: application/json%\r%\nContent%-Length: %d+%\r%\n%\r%\n{%s*\"[A-Za-z]+\"%s*:%s*\".+\"%s*,%s*\"[A-Za-z]+\"%s*:%s*\".+\"%s*}";

Есть ли лучший/рекомендуемый способ проверки и анализа HTTP-запроса? Это должна быть проблема, с которой другие уже столкнулись и решили. Пожалуйста, опубликуйте фрагмент кода, решающий эту проблему, если это возможно.


person Saqib Ali    schedule 26.03.2020    source источник
comment
Извините, но ваш JSON никоим образом не является допустимым JSON (пробел, разрывы строк) - я выполняю синтаксический анализ/проверку JSON вручную с массивами символов, но он работает только с действительными JSON. И для уточнения, какие части входящих вам действительно нужны, и можете ли вы предоставить действительные данные заголовка (например, без пробела после ':'). Спасибо.   -  person Codebreaker007    schedule 26.03.2020
comment
Мне нужно выбрать два поля данных. Поэтому мне нужно знать, что A=B и C=D. Остальное только проверка.   -  person Saqib Ali    schedule 26.03.2020
comment
Остальное - просто проверка того, что вам действительно нужно обработать, потому что это определяет сложность решения - переходя к вопросу - почему бы не взять существующую проверенную библиотеку и не адаптировать код к вашим особым потребностям = Открытый исходный код вместо того, чтобы изобретать велосипед . Для синтаксического анализа JSON требуется действительный JSON заранее, иначе вы потерпите неудачу - я мог бы предоставить рабочие примеры, если знаю, что вы намереваетесь.   -  person Codebreaker007    schedule 26.03.2020
comment
@ Codebreaker007 Я спрашиваю вас, какую проверенную библиотеку мне следует использовать. Мне нужно обработать значения B и D. ОК, входящий запрос недействителен JSON. Я понимаю. Мне все равно нужно разбирать запросы, которые выглядят так.   -  person Saqib Ali    schedule 26.03.2020
comment
Наиболее часто используемой библиотекой для обработки объектов json является ArduinoJson, конкретно для вашего случая вы можете прочитать этот JsonParser пример.   -  person hcheung    schedule 27.03.2020


Ответы (2)


Для начала:
Сначала отправьте правильный (синтаксис!) тестовый запрос.

curl \
 request POST \
header "Content-Type:application/json" \
data '{"A":"B","C":"D"}' \
"http://192.168.4.1/myEndpoint"

есть множество отличных примеров, если вы выполните поиск:

Ethernet-библиотека веб-сервера arduino

в вашей любимой поисковой системе.
Одним из них может быть: https://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/
или вы используете библиотеку веб-сервера из esp8266 и адаптируете ее (не очень сложно имхо)
вы бы сделали на Arduino что-то вроде

webServer.on("/myroute/lighton", HTTP_POST, readJson);

char jsonField[64] = '\0'; //This is a predefined buffer for data handilng

как будет выглядеть функция (частично рабочий код, частично псевдокод)

bool readJson(){
 if (webserver.args() == 0) return false;  // we could do in the caller an error handling on that
 strcpy (jsonField, webserver.arg(1).c_str());  // here we copy the json to a buffer

 /** Get rid of starting and finishing bracket and copy to */
   strncpy(jsonField , jsonField + 1, strlen(jsonField) - 2);
  jsonField[strlen(jsonField) - 2] = '\0';
   uint16_t maxIndex = strlen(jsonField); // number of characters received - without the brackets
     uint16_t index = 0;
  int16_t nextIndex = 0;
  uint8_t  i = 0;
  // In this routine we get the value pairs e.g. "A":"B"
  while ((nextIndex != -1) && (nextIndex < maxIndex)) {
    nextIndex = indexOf(jsonField, ',', index);

    ... the next step would be to process the value pairs by stripping the " and split on the ':' delimiter --
    if you need just the values = content in your example B and D its easy, 
    you could do 
    if (strcmp (firstValofPair ,'A')==0) valueB = atoi(B); // given B is a number and we have to convert from char to int

    .... some more logic and you have a simple reliable JSON Parser for all kind of web server usage

  }
 return true; // success parsing
}

Я реализовал такую ​​логику в некоторых реальных живых сценариях, и все они работают надежно и стабильно уже несколько лет. И последний совет:
Никогда не используйте класс Arduino String в сценариях веб-сервера Arduino. Класс String ломает вашу кучу и ломает ваш Arduino. В моем примере я использую фиксированные символы, которые компилируются в стек и поддерживают вашу память.

person Codebreaker007    schedule 26.03.2020

Связанная библиотека Regexp основана на Lua, в которой есть интересный оператор сопоставления с образцом.

%b() сбалансированная вложенная пара ( ... ( ... ) ... )

Затем, чтобы получить сбалансированное выражение фигурных скобок в конце HTTP-запроса, используйте выражение

"(%b{})%s*$"

Как указано в комментариях, это не означает, что захват является допустимым JSON. Следующим шагом будет передача захваченной подстроки синтаксическому анализатору JSON для проверки.

person Vincent Lucarelli    schedule 26.03.2020