added a ResponseParser

master
Kenneth Barbour 2020-07-03 12:35:02 -04:00
parent 2d499714d5
commit b00c493206
3 changed files with 166 additions and 0 deletions

View File

@ -0,0 +1,92 @@
#include "ResponseParser.h"
void ResponseParser::reset() {
_state = S_IN_HTTPVER;
_i = _buffer;
}
bool ResponseParser::parse() {
char in;
while (server.available() && _state != S_ERROR) {
switch (_state) {
case S_IN_HTTPVER:
in = server.read();
if (in == ' ') {
*(_i++) = '\0';
response.setHttpVersion(_buffer);
_i = _buffer;
_state = S_IN_CODE;
break;
}
*(_i++) = in;
break;
case S_IN_CODE:
in = server.read();
// skip preceding whitespace
if (in == ' ') {
if (_i == _buffer) continue;
*(_i) = '\0';
response.code = atoi(_buffer);
_i = _buffer;
_state = S_IN_REASON;
break;
}
*(_i++) = in;
break;
case S_IN_REASON:
in = server.read();
if (in == '\r') continue;
if (in == '\n') {
*(_i) = '\0';
response.setReason(_buffer);
_i = _buffer;
_state = S_IN_HEADER;
break;
}
*(_i++) = in;
break;
case S_IN_HEADER:
in = server.read();
if (in == '\r') continue;
if (in == '\n') {
if (_i == _buffer) {
_state = S_IN_MESSAGE;
_i = _buffer;
break;
}
*(_i) = '\0';
char * name = _buffer;
char * value = _buffer;
bool s = 0;
while (value < _i) {
if (s) {
if (*value == ' ') value++;
else break;
} else {
if (*value == ':') {
s = true;
*(value++) = '\0';
} else value++;
}
}
response.headers.set(name, value);
_i = _buffer;
break;
}
*(_i++) = in;
break;
case S_IN_MESSAGE:
while (server.available()) {
response.content->write(server.read());
}
_state = S_COMPLETE;
case S_COMPLETE:
return true;
break;
default:
_state = S_ERROR;
}
}
return (_state == S_COMPLETE || _state == S_ERROR);
}

View File

@ -0,0 +1,40 @@
#pragma once
#include "HttpResponse.h"
#include "Arduino.h"
#include "Stream.h"
#ifndef RESPONSEPARSER_BUFFER
#define RESPONSEPARSER_BUFFER 512
#endif
enum ResponseParserState {
S_IN_HTTPVER,
S_IN_CODE,
S_IN_REASON,
S_IN_HEADER,
S_IN_MESSAGE,
S_COMPLETE,
S_ERROR
};
class ResponseParser
{
public:
ResponseParser(HttpResponse& res, Stream& server):
response(res),
server(server),
_state(S_IN_HTTPVER)
{};
bool parse();
bool error() { return _state == S_ERROR; };
void reset();
private:
char _buffer[RESPONSEPARSER_BUFFER] = {};
char * _i = _buffer;
HttpResponse& response;
Stream& server;
ResponseParserState _state;
};

View File

@ -0,0 +1,34 @@
#include "catch.hpp"
#include "ResponseParser.h"
#include <HttpResponse.h>
#include <Buffer.h>
using Catch::Matchers::Equals;
TEST_CASE("Test parsing a response with headers and content")
{
uint8_t _reqbuff[100];
Buffer server(_reqbuff, 100);
uint8_t _resbuff[100];
Buffer buffer(_resbuff, 100);
HttpResponse response;
ResponseParser parser(response, server);
response.content = &buffer;
server.print("HTTP/1.0 200 OK\r\nCookie: foo=bar; baz=qux;\r\nContent-Length:10\r\n\r\nABCDEFGHIJ");
bool result = parser.parse();
CHECK_THAT(response.getHttpVersion(), Equals("HTTP/1.0"));
CHECK(response.code == 200);
CHECK_THAT(response.getReason(), Equals("OK"));
CHECK(response.headers.count() == 2);
CHECK(response.headers.has("Cookie") == true);
CHECK_THAT(response.headers.get("Cookie"), Equals("foo=bar; baz=qux;"));
CHECK(response.headers.has("Content-Length") == true);
CHECK_THAT(response.headers.get("Content-Length"), Equals("10"));
CHECK_THAT((char*)_resbuff, Equals("ABCDEFGHIJ"));
}