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