A RequestParser that builds a request from interrupted input from a Stream
parent
2a6dd718f7
commit
c92d39ddb1
|
@ -0,0 +1,81 @@
|
|||
#include "RequestParser.h"
|
||||
|
||||
bool RequestParser::parse() {
|
||||
|
||||
while (client.available() && _state != S_ERROR) {
|
||||
char in = client.read();
|
||||
|
||||
switch (_state) {
|
||||
case S_IN_METHOD:
|
||||
if (in == ' ') {
|
||||
*(_i++) = '\0';
|
||||
request.setMethod(_buffer);
|
||||
_i = _buffer;
|
||||
_state = S_IN_URL;
|
||||
break;
|
||||
}
|
||||
*(_i++) = in;
|
||||
break;
|
||||
case S_IN_URL:
|
||||
// skip preceding whitespace
|
||||
if (in == ' ') {
|
||||
if (_i == _buffer) continue;
|
||||
*(_i) = '\0';
|
||||
request.setUrl(_buffer);
|
||||
_i = _buffer;
|
||||
_state = S_IN_HTTPVER;
|
||||
break;
|
||||
}
|
||||
*(_i++) = in;
|
||||
break;
|
||||
case S_IN_HTTPVER:
|
||||
if (in == '\r') continue;
|
||||
if (in == '\n') {
|
||||
*(_i) = '\0';
|
||||
request.setHttpVer(_buffer);
|
||||
_i = _buffer;
|
||||
_state = S_IN_HEADER;
|
||||
break;
|
||||
}
|
||||
*(_i++) = in;
|
||||
break;
|
||||
case S_IN_HEADER:
|
||||
if (in == '\r') continue;
|
||||
if (in == '\n') {
|
||||
if (_i == _buffer) {
|
||||
if (request.headers.has("Content-Length"))
|
||||
_state = S_IN_MESSAGE;
|
||||
else _state = S_COMPLETE;
|
||||
break;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
}
|
||||
request.headers.set(name, value);
|
||||
_i = _buffer;
|
||||
break;
|
||||
}
|
||||
*(_i++) = in;
|
||||
break;
|
||||
case S_COMPLETE:
|
||||
return true;
|
||||
default:
|
||||
_state = S_ERROR;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (_state == S_COMPLETE || _state == S_ERROR);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
#include "HttpRequest.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifndef REQUESTPARSER_BUFFER
|
||||
#define REQUESTPARSER_BUFFER 512
|
||||
#endif
|
||||
|
||||
enum RequestParserState {
|
||||
S_IN_METHOD,
|
||||
S_IN_URL,
|
||||
S_IN_HTTPVER,
|
||||
S_IN_HEADER,
|
||||
S_IN_MESSAGE,
|
||||
S_COMPLETE,
|
||||
S_ERROR
|
||||
};
|
||||
|
||||
class RequestParser
|
||||
{
|
||||
public:
|
||||
RequestParser(HttpRequest& req, Stream& client):
|
||||
request(req),
|
||||
client(client),
|
||||
_state(S_IN_METHOD)
|
||||
{};
|
||||
|
||||
bool parse();
|
||||
bool error() { return _state == S_ERROR; };
|
||||
|
||||
|
||||
private:
|
||||
char _buffer[REQUESTPARSER_BUFFER] = {};
|
||||
char * _i = _buffer;
|
||||
HttpRequest& request;
|
||||
Stream& client;
|
||||
RequestParserState _state;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
#include "catch.hpp"
|
||||
#include "Buffer.h"
|
||||
#include "RequestParser.h"
|
||||
using Catch::Matchers::Equals;
|
||||
|
||||
TEST_CASE("Test GET","[RequestParser]")
|
||||
{
|
||||
uint8_t _buff[256] = {};
|
||||
Buffer client(_buff, 256);
|
||||
HttpRequest request;
|
||||
RequestParser parser(request, client);
|
||||
|
||||
client.write("GET ");
|
||||
CHECK(parser.parse() == 0);
|
||||
CHECK_THAT(request.getMethod(), Equals("GET"));
|
||||
|
||||
client.write("/foo ");
|
||||
CHECK(parser.parse() == 0);
|
||||
CHECK_THAT(request.getUrl(), Equals("/foo"));
|
||||
|
||||
client.write("HTTP/1.1\r\n");
|
||||
CHECK(parser.parse() == 0);
|
||||
CHECK_THAT(request.getHttpVer(), Equals("HTTP/1.1"));
|
||||
|
||||
client.write("Host: localhost\r\n");
|
||||
CHECK(parser.parse() == 0);
|
||||
CHECK_THAT(request.headers.get("Host"), Equals("localhost"));
|
||||
|
||||
client.write("\r\n");
|
||||
CHECK(parser.parse());
|
||||
CHECK(!parser.error());
|
||||
}
|
Loading…
Reference in New Issue