Commit cf26bf91 authored by octal's avatar octal

Now parsing query parameters in the Uri

parent feb47116
......@@ -24,7 +24,7 @@ class MyHandler : public Net::Http::Handler {
}
else if (req.resource() == "/echo") {
if (req.method() == Net::Http::Method::Post) {
response.send(Net::Http::Code::Ok, req.body());
response.send(Net::Http::Code::Ok, req.body(), MIME(Text, Plain));
}
}
else if (req.resource() == "/exception") {
......
......@@ -27,6 +27,17 @@ std::basic_ostream<CharT, Traits>& crlf(std::basic_ostream<CharT, Traits>& os) {
os.write(CRLF, 2);
}
template<typename H, typename Stream, typename... Args>
typename std::enable_if<Header::IsHeader<H>::value, void>::type
writeHeader(Stream& stream, Args&& ...args) {
H header(std::forward<Args>(args)...);
stream << H::Name << ": ";
header.write(stream);
stream << crlf;
}
static constexpr const char* ParserData = "__Parser";
namespace Private {
......@@ -77,32 +88,54 @@ namespace Private {
if (!cursor.advance(1)) return State::Again;
size_t start = cursor;
StreamCursor::Token resToken(cursor);
while ((n = cursor.current()) != '?' && n != ' ')
if (!cursor.advance(1)) return State::Again;
request->resource_ = resToken.text();
while ((n = cursor.next()) != StreamCursor::Eof && n != ' ') {
// Query parameters of the Uri
if (n == '?') {
if (!cursor.advance(1)) return State::Again;
}
request->resource_ = std::string(cursor.offset(start), cursor.diff(start) + 1);
if ((n = cursor.next()) == StreamCursor::Eof) return State::Again;
while ((n = cursor.current()) != ' ') {
StreamCursor::Token keyToken(cursor);
if (!match_until('=', cursor))
return State::Again;
if (n != ' ')
raise("Malformed HTTP request after Request-URI");
std::string key = keyToken.text();
if (!cursor.advance(1)) return State::Again;
StreamCursor::Token valueToken(cursor);
if (!match_until({ ' ', '&' }, cursor))
return State::Again;
std::string value = valueToken.text();
request->query_.add(std::move(key), std::move(value));
if (cursor.current() == '&') {
if (!cursor.advance(1)) return State::Again;
}
}
}
// @Todo: Fragment
// SP
if (!cursor.advance(2)) return State::Again;
if (!cursor.advance(1)) return State::Again;
// HTTP-Version
start = cursor;
StreamCursor::Token versionToken(cursor);
while (!cursor.eol())
if (!cursor.advance(1)) return State::Again;
const size_t diff = cursor.diff(start);
if (strncmp(cursor.offset(start), "HTTP/1.0", diff) == 0) {
const char* ver = versionToken.rawText();
const size_t size = versionToken.size();
if (strncmp(ver, "HTTP/1.0", size) == 0) {
request->version_ = Version::Http10;
}
else if (strncmp(cursor.offset(start), "HTTP/1.1", diff) == 0) {
else if (strncmp(ver, "HTTP/1.1", size) == 0) {
request->version_ = Version::Http11;
}
else {
......@@ -256,6 +289,31 @@ Message::Message()
: version_(Version::Http11)
{ }
namespace Uri {
Query::Query()
{ }
Query::Query(std::initializer_list<std::pair<const std::string, std::string>> params)
: params(params)
{ }
void
Query::add(std::string name, std::string value) {
params.insert(std::make_pair(std::move(name), std::move(value)));
}
Optional<std::string>
Query::get(const std::string& name) const {
auto it = params.find(name);
if (it == std::end(params))
return None();
return Some(it->second);
}
} // namespace Uri
Request::Request()
: Message()
{ }
......@@ -285,6 +343,11 @@ Request::headers() const {
return headers_;
}
const Uri::Query&
Request::query() const {
return query_;
}
Response::Response()
: Message()
{ }
......@@ -306,8 +369,6 @@ Response::send(Code code) {
ssize_t
Response::send(Code code, const std::string& body, const Mime::MediaType& mime)
{
int fd = peer()->fd();
char buffer[Const::MaxBuffer];
Io::OutArrayBuf obuf(buffer, Io::Init::ZeroOut);
......@@ -318,6 +379,15 @@ Response::send(Code code, const std::string& body, const Mime::MediaType& mime)
stream << code;
stream << crlf;
if (mime.isValid()) {
auto contentType = headers_.tryGet<Header::ContentType>();
if (contentType)
contentType->setMime(mime);
else {
writeHeader<Header::ContentType>(stream, mime);
}
}
for (const auto& header: headers_.list()) {
stream << header->name() << ": ";
header->write(stream);
......@@ -325,14 +395,15 @@ Response::send(Code code, const std::string& body, const Mime::MediaType& mime)
}
if (!body.empty()) {
stream << "Content-Length: " << body.size() << crlf;
writeHeader<Header::ContentLength>(stream, body.size());
stream << crlf;
stream << body;
}
else {
stream << crlf;
}
ssize_t bytes = ::send(fd, buffer, obuf.len(), 0);
return bytes;
return peer()->send(buffer, obuf.len());
}
void
......
......@@ -37,6 +37,22 @@ public:
std::string body_;
};
namespace Uri {
typedef std::string Fragment;
class Query {
public:
Query();
Query(std::initializer_list<std::pair<const std::string, std::string>> params);
void add(std::string name, std::string value);
Optional<std::string> get(const std::string& name) const;
private:
std::unordered_map<std::string, std::string> params;
};
} // namespace Uri
// 5. Request
class Request : private Message {
public:
......@@ -54,10 +70,12 @@ public:
std::string body() const;
const Header::Collection& headers() const;
const Uri::Query& query() const;
private:
Method method_;
std::string resource_;
Uri::Query query_;
};
class Handler;
......
......@@ -237,6 +237,7 @@ public:
void write(std::ostream& os) const;
Mime::MediaType mime() const { return mime_; }
void setMime(const Mime::MediaType& mime) { mime_ = mime; }
private:
Mime::MediaType mime_;
......
......@@ -271,6 +271,11 @@ MediaType::toString() const {
return res;
}
bool
MediaType::isValid() const {
return top_ != Type::None && sub_ != Subtype::None;
}
} // namespace Mime
} // namespace Http
......
......@@ -173,6 +173,7 @@ public:
void setParam(std::string name, std::string value);
std::string toString() const;
bool isValid() const;
private:
Mime::Type top_;
......
......@@ -6,6 +6,7 @@
#include "peer.h"
#include <iostream>
#include <stdexcept>
#include <sys/socket.h>
namespace Net {
......@@ -74,6 +75,12 @@ Peer::tryGetData(std::string(name)) const {
return it->second;
}
ssize_t
Peer::send(const void* buf, size_t len) {
return ::send(fd_, buf, len, 0);
}
std::ostream& operator<<(std::ostream& os, const Peer& peer) {
const auto& addr = peer.address();
os << "(" << addr.host() << ", " << addr.port() << ") [" << peer.hostname() << "]";
......
......@@ -45,6 +45,8 @@ public:
return std::static_pointer_cast<T>(data);
}
ssize_t send(const void* buf, size_t len);
private:
Address addr;
......
......@@ -98,9 +98,17 @@ public:
return cursor.value;
}
size_t size() const {
return cursor.value - pos;
}
std::string text() {
const char *beg = cursor.offset(pos);
return std::string(beg, end() - start());
return std::string(beg, size());
}
const char* rawText() const {
return cursor.offset(pos);
}
private:
......
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