Commit 0329db6a authored by Mathieu STEFANI's avatar Mathieu STEFANI

Body is now correctly parsed. Not fully tested but let's pretend it works

parent 40a6cba8
......@@ -21,6 +21,13 @@ class MyHandler : public Net::Http::Handler {
}
}
else if (req.resource == "/echo") {
if (req.method == Net::Http::Method::Post) {
Net::Http::Response response(Net::Http::Code::Ok, req.body);
response.writeTo(peer);
}
}
}
};
......
......@@ -30,13 +30,20 @@ namespace Private {
memset(data, sizeof data, 0);
}
void
Parser::Buffer::reset() {
memset(data, sizeof data, 0);
len = 0;
}
bool
Parser::Cursor::advance(size_t count)
{
if (value + count >= sizeof (buff.data)) {
//parser->raise("Early EOF");
}
else if (value + count >= buff.len) {
// Allowed to advance one past the end
else if (value + count > buff.len) {
return false;
}
......@@ -78,8 +85,24 @@ namespace Private {
}
size_t
Parser::Cursor::diff(size_t previous) const {
return value - previous;
Parser::Cursor::diff(size_t other) const {
return value - other;
}
size_t
Parser::Cursor::diff(const Cursor& other) const {
return value - other.value;
}
size_t
Parser::Cursor::remaining() const {
// assert(val <= buff.len);
return buff.len - value;
}
void
Parser::Cursor::reset() {
value = 0;
}
void
......@@ -215,19 +238,54 @@ namespace Private {
Parser::State
Parser::BodyStep::apply(Cursor& cursor) {
auto cl = request->headers.tryGet<ContentLength>();
if (!cl) return Parser::State::Done;
if (cl) {
// CRLF
auto contentLength = cl->value();
// We already started to read some bytes but we got an incomplete payload
if (bytesRead > 0) {
// How many bytes do we still need to read ?
const size_t remaining = contentLength - bytesRead;
auto start = cursor;
// Could be refactored in a single function / lambda but I'm too lazy
// for that right now
if (!cursor.advance(remaining)) {
const size_t available = cursor.remaining();
request->body.append(cursor.offset(start), available);
bytesRead += available;
cursor.advance(available);
return State::Again;
}
else {
request->body.append(cursor.offset(), cursor.diff(start));
}
}
// This is the first time we are reading the payload
else {
if (!cursor.advance(2)) return State::Again;
auto len = cl->value();
request->body.reserve(contentLength);
auto start = cursor;
if (!cursor.advance(len)) return State::Again;
// We have an incomplete body, read what we can
if (!cursor.advance(contentLength)) {
const size_t available = cursor.remaining();
request->body.append(cursor.offset(start), available);
bytesRead += available;
cursor.advance(available);
return State::Again;
}
request->body = std::string(cursor.offset(start), cursor.diff(start));
request->body.append(cursor.offset(start), cursor.diff(start));
}
bytesRead = 0;
return Parser::State::Done;
}
......@@ -254,6 +312,19 @@ namespace Private {
memcpy(buffer.data + buffer.len, data, len);
buffer.len += len;
return true;
}
void
Parser::reset() {
buffer.reset();
cursor.reset();
currentStep = 0;
request.headers.clear();
request.body.clear();
request.resource.clear();
}
ssize_t
......@@ -366,14 +437,19 @@ Response::writeTo(Tcp::Peer& peer)
void
Handler::onInput(const char* buffer, size_t len, Tcp::Peer& peer) {
Private::Parser parser(buffer, len);
try {
Private::Parser::State state = parser.parse();
if (state == Private::Parser::State::Done) {
onRequest(parser.request, peer);
if (!parser.feed(buffer, len)) {
cout << "Could not feed parser bro" << endl;
}
else {
try {
auto state = parser.parse();
if (state == Private::Parser::State::Done) {
onRequest(parser.request, peer);
parser.reset();
}
} catch (const Private::ParsingError &err) {
cerr << "Error when parsing HTTP request: " << err.what() << endl;
}
} catch (const Private::ParsingError &err) {
cerr << "Error when parsing HTTP request: " << err.what() << endl;
}
}
......
......@@ -135,6 +135,8 @@ namespace Private {
char data[Const::MaxBuffer];
size_t len;
void reset();
};
struct Cursor {
......@@ -182,8 +184,13 @@ namespace Private {
const char *offset() const;
const char *offset(size_t off) const;
size_t diff(size_t before) const;
size_t diff(size_t other) const;
size_t diff(const Cursor& other) const;
size_t remaining() const;
void reset();
private:
const Buffer& buff;
size_t value;
};
......@@ -221,11 +228,25 @@ namespace Private {
struct BodyStep : public Step {
BodyStep(Request* request)
: Step(request)
, bytesRead(0)
{ }
State apply(Cursor& cursor);
private:
size_t bytesRead;
};
Parser()
: contentLength(-1)
, currentStep(0)
, cursor(buffer)
{
allSteps[0].reset(new RequestLineStep(&request));
allSteps[1].reset(new HeadersStep(&request));
allSteps[2].reset(new BodyStep(&request));
}
Parser(const char* data, size_t len)
: contentLength(-1)
, currentStep(0)
......@@ -239,6 +260,7 @@ namespace Private {
}
bool feed(const char* data, size_t len);
void reset();
State parse();
......@@ -305,6 +327,9 @@ public:
void onOutput();
virtual void onRequest(const Request& request, Tcp::Peer& peer) = 0;
private:
Private::Parser parser;
};
class Endpoint {
......
......@@ -116,6 +116,11 @@ Headers::list() const {
return ret;
}
void
Headers::clear() {
headers.clear();
}
std::pair<bool, std::shared_ptr<Header>>
Headers::getImpl(const std::string& name) const {
auto it = headers.find(name);
......
......@@ -67,6 +67,8 @@ public:
std::vector<std::shared_ptr<Header>> list() const;
void clear();
private:
std::pair<bool, std::shared_ptr<Header>> getImpl(const std::string& name) const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment