diff --git a/src/ResponseParser.cpp b/src/ResponseParser.cpp new file mode 100644 index 0000000..db95631 --- /dev/null +++ b/src/ResponseParser.cpp @@ -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); +} \ No newline at end of file diff --git a/src/ResponseParser.h b/src/ResponseParser.h new file mode 100644 index 0000000..fbc8c2b --- /dev/null +++ b/src/ResponseParser.h @@ -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; +}; diff --git a/test/test_ResponseParser.cpp b/test/test_ResponseParser.cpp new file mode 100644 index 0000000..48954f8 --- /dev/null +++ b/test/test_ResponseParser.cpp @@ -0,0 +1,34 @@ +#include "catch.hpp" +#include "ResponseParser.h" +#include +#include + +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")); + +} \ No newline at end of file