From 8c4ac1a6f0e154ebcbc72e196d24df322da673bc Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 22 Jan 2015 16:20:00 +0200 Subject: [PATCH 01/16] Make Request API consistent with itself and others Request::from_url was incorrectly creating requests with unencoded URLs. --- source/http/request.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/http/request.cpp b/source/http/request.cpp index 0194859..f050e04 100644 --- a/source/http/request.cpp +++ b/source/http/request.cpp @@ -12,7 +12,10 @@ namespace Http { Request::Request(const string &m, const string &p): method(m), path(p) -{ } +{ + if(path.find(' ')!=string::npos) + throw invalid_argument("Request::Request"); +} string Request::str() const { @@ -45,7 +48,7 @@ Request Request::from_url(const string &str) if(url.scheme!="http") throw invalid_argument("Request::from_url"); - string path = url.path; + string path = urlencode(url.path); if(path.empty()) path = "/"; if(!url.query.empty()) -- 2.43.0 From 0165ed331051ae7fc64dfe85dd7ab8f5b11ba919 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 22 Jan 2015 16:23:19 +0200 Subject: [PATCH 02/16] Implement connection keep-alive in HttpServer --- source/http/server.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/http/server.cpp b/source/http/server.cpp index b18ed4c..e0b34b3 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -121,8 +121,12 @@ void Server::client_data_available(Client &cl) cl.in_buf.erase(0, len); } + bool keepalive = false; if(cl.request && cl.request->is_complete() && !response) { + if(cl.request->has_header("Connection")) + keepalive = (cl.request->get_header("Connection")=="keep-alive"); + response = new Response(NONE); try { @@ -154,7 +158,18 @@ void Server::client_data_available(Client &cl) if(response) { cl.sock->write(response->str()); - cl.stale = true; + if(keepalive) + { + delete cl.request; + cl.request = 0; + delete cl.response; + cl.response = 0; + } + else + { + cl.sock->shutdown(IO::M_WRITE); + cl.stale = true; + } } } -- 2.43.0 From a0b7f9bcf0654a7a3f24bac95746ac587b71986b Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 7 Apr 2015 01:19:21 +0300 Subject: [PATCH 03/16] Provide a signal to handle errors from Communicator Since Communicator is typically used with an EventDispatcher, it can be inconvenient to just throw exceptions out. Handling them would require wrapping the EventDispatcher's tick call in a try block, and the exact source of the exception would be lost. --- source/net/communicator.cpp | 11 ++++++++--- source/net/communicator.h | 13 ++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/source/net/communicator.cpp b/source/net/communicator.cpp index c9b277d..618e8b4 100644 --- a/source/net/communicator.cpp +++ b/source/net/communicator.cpp @@ -1,6 +1,8 @@ #include #include "communicator.h" +using namespace std; + namespace { using namespace Msp::Net; @@ -85,9 +87,10 @@ void Communicator::data_available() if(!good) return; - in_end += socket.read(in_end, in_buf+buf_size-in_end); try { + in_end += socket.read(in_end, in_buf+buf_size-in_end); + bool more = true; while(more) { @@ -114,10 +117,12 @@ void Communicator::data_available() } } } - catch(...) + catch(const exception &e) { good = false; - throw; + if(signal_error.empty()) + throw; + signal_error.emit(e); } } diff --git a/source/net/communicator.h b/source/net/communicator.h index 8530db1..cd465a5 100644 --- a/source/net/communicator.h +++ b/source/net/communicator.h @@ -19,6 +19,7 @@ class Communicator { public: sigc::signal signal_handshake_done; + sigc::signal signal_error; private: StreamSocket &socket; @@ -47,7 +48,17 @@ public: if(handshake_status!=2) throw sequence_error("handshaking not done"); unsigned size = protocol.assemble(pkt, out_buf, buf_size); - socket.write(out_buf, size); + try + { + socket.write(out_buf, size); + } + catch(const std::exception &e) + { + good = false; + if(signal_error.empty()) + throw; + signal_error.emit(e); + } } private: -- 2.43.0 From 8286d850ba8451e6acdad5f2083b8fb324e84717 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 7 Apr 2015 01:23:12 +0300 Subject: [PATCH 04/16] Correct a typo in a multiple inclusion guard --- source/http/response.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/http/response.h b/source/http/response.h index d721ad8..bb471f0 100644 --- a/source/http/response.h +++ b/source/http/response.h @@ -1,5 +1,5 @@ #ifndef MSP_HTTP_RESPONSE_H_ -#define MSP_HTTO_RESPONSE_H_ +#define MSP_HTTP_RESPONSE_H_ #include "message.h" #include "status.h" -- 2.43.0 From 5f6c5ae5527619f7ffc82a69cf73d3fc7347e51f Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 7 Apr 2015 01:24:18 +0300 Subject: [PATCH 05/16] Reject requests with a relative path As per the HTTP specification --- source/http/server.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/http/server.cpp b/source/http/server.cpp index e0b34b3..ed3a2b9 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -106,6 +106,11 @@ void Server::client_data_available(Client &cl) response = new Response(NOT_IMPLEMENTED); response->add_content("Method not implemented\n"); } + else if(cl.request->get_path()[0]!='/') + { + response = new Response(BAD_REQUEST); + response->add_content("Path must be absolute\n"); + } } catch(const exception &e) { -- 2.43.0 From dd8b0e7471bb85d26cc63da2f0819ceeff30550f Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 7 Apr 2015 13:58:25 +0300 Subject: [PATCH 06/16] Include winsock2.h first It wants to get included before windows.h --- source/net/datagramsocket.cpp | 3 +++ source/net/socket.cpp | 4 +++- source/net/streamserversocket.cpp | 3 +++ source/net/streamsocket.cpp | 4 +++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/net/datagramsocket.cpp b/source/net/datagramsocket.cpp index d24ca4e..664c265 100644 --- a/source/net/datagramsocket.cpp +++ b/source/net/datagramsocket.cpp @@ -1,3 +1,6 @@ +#ifdef WIN32 +#include +#endif #include #include #include diff --git a/source/net/socket.cpp b/source/net/socket.cpp index 99a2bc4..7c16217 100644 --- a/source/net/socket.cpp +++ b/source/net/socket.cpp @@ -1,4 +1,6 @@ -#ifndef WIN32 +#ifdef WIN32 +#include +#else #include #include #include diff --git a/source/net/streamserversocket.cpp b/source/net/streamserversocket.cpp index bbf91bc..8039ec9 100644 --- a/source/net/streamserversocket.cpp +++ b/source/net/streamserversocket.cpp @@ -1,3 +1,6 @@ +#ifdef WIN32 +#include +#endif #include #include #include diff --git a/source/net/streamsocket.cpp b/source/net/streamsocket.cpp index 3da0cc3..1dab2e3 100644 --- a/source/net/streamsocket.cpp +++ b/source/net/streamsocket.cpp @@ -1,4 +1,6 @@ -#ifndef WIN32 +#ifdef WIN32 +#include +#else #include #endif #include -- 2.43.0 From 49fbad8c6fbe8c54c9b2dafff48eb14d36a5aa74 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 10 Apr 2015 21:08:22 +0300 Subject: [PATCH 07/16] Set event types on Windows if connect finished immediately --- source/net/streamsocket.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/net/streamsocket.cpp b/source/net/streamsocket.cpp index 1dab2e3..85f2b00 100644 --- a/source/net/streamsocket.cpp +++ b/source/net/streamsocket.cpp @@ -73,6 +73,9 @@ bool StreamSocket::connect(const SockAddr &addr) if(err==0) { connected = true; +#ifdef WIN32 + WSAEventSelect(priv->handle, *priv->event, FD_READ|FD_CLOSE); +#endif set_events(IO::P_INPUT); signal_connect_finished.emit(0); } -- 2.43.0 From abc40062b2be394ce007711be98e221167317aa1 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 19 Nov 2015 10:17:18 +0200 Subject: [PATCH 08/16] Use a typecast to avoid a signedness mismatch warning --- source/net/unix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/net/unix.cpp b/source/net/unix.cpp index 373b65a..cd1f414 100644 --- a/source/net/unix.cpp +++ b/source/net/unix.cpp @@ -26,7 +26,7 @@ UnixAddr::UnixAddr(const SysAddr &sa): throw logic_error("no unix sockets on windows"); #else const sockaddr_un &sau = reinterpret_cast(sa.addr); - if(sa.size>sizeof(sa_family_t)) + if(static_cast(sa.size)>sizeof(sa_family_t)) { abstract = (sau.sun_path[0]==0); path.assign(sau.sun_path+abstract, sa.size-sizeof(sa_family_t)-abstract); -- 2.43.0 From f839e84e68924129a9fa5941ad82e4e9cc1def4d Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Apr 2016 10:31:27 +0300 Subject: [PATCH 09/16] Hide Http::Server destructor in the library Otherwise sigc::signal destructor will be emitted in the including file, requiring explicit linking with libsigc++. --- source/http/server.cpp | 5 +++++ source/http/server.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/source/http/server.cpp b/source/http/server.cpp index ed3a2b9..9bdfa61 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -23,6 +23,11 @@ Server::Server(unsigned port): sock.listen(*addr, 8); } +// Avoid emitting sigc::signal destructor in files including server.h +Server::~Server() +{ +} + unsigned Server::get_port() const { const Net::SockAddr &addr = sock.get_local_address(); diff --git a/source/http/server.h b/source/http/server.h index e07ee55..04c2d06 100644 --- a/source/http/server.h +++ b/source/http/server.h @@ -37,6 +37,8 @@ private: public: Server(unsigned); + ~Server(); + unsigned get_port() const; void use_event_dispatcher(IO::EventDispatcher *); void delay_response(Response &); -- 2.43.0 From 09858e5a153b0667b4885da81f6f979a0bf29c36 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Apr 2016 10:34:42 +0300 Subject: [PATCH 10/16] Use connection keepalive on async responses too --- source/http/server.cpp | 40 +++++++++++++++++++++------------------- source/http/server.h | 2 ++ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/source/http/server.cpp b/source/http/server.cpp index 9bdfa61..fbacb10 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -62,10 +62,7 @@ void Server::submit_response(Response &resp) { Client &cl = get_client_by_response(resp); if(cl.async) - { - cl.sock->write(resp.str()); - cl.stale = true; - } + send_response(cl, *cl.response); } void Server::data_available() @@ -131,11 +128,11 @@ void Server::client_data_available(Client &cl) cl.in_buf.erase(0, len); } - bool keepalive = false; if(cl.request && cl.request->is_complete() && !response) { + cl.keepalive = false; if(cl.request->has_header("Connection")) - keepalive = (cl.request->get_header("Connection")=="keep-alive"); + cl.keepalive = (cl.request->get_header("Connection")=="keep-alive"); response = new Response(NONE); try @@ -166,20 +163,24 @@ void Server::client_data_available(Client &cl) } if(response) + send_response(cl, *response); +} + +void Server::send_response(Client &cl, Response &resp) +{ + cl.sock->write(resp.str()); + cl.async = false; + if(cl.keepalive) { - cl.sock->write(response->str()); - if(keepalive) - { - delete cl.request; - cl.request = 0; - delete cl.response; - cl.response = 0; - } - else - { - cl.sock->shutdown(IO::M_WRITE); - cl.stale = true; - } + delete cl.request; + cl.request = 0; + delete cl.response; + cl.response = 0; + } + else + { + cl.sock->shutdown(IO::M_WRITE); + cl.stale = true; } } @@ -198,6 +199,7 @@ Server::Client::Client(RefPtr s): sock(s), request(0), response(0), + keepalive(false), async(false), stale(false) { } diff --git a/source/http/server.h b/source/http/server.h index 04c2d06..29b2b45 100644 --- a/source/http/server.h +++ b/source/http/server.h @@ -23,6 +23,7 @@ private: std::string in_buf; Request *request; Response *response; + bool keepalive; bool async; bool stale; @@ -46,6 +47,7 @@ public: private: void data_available(); void client_data_available(Client &); + void send_response(Client &, Response &); void client_end_of_file(Client &); Client &get_client_by_response(Response &); }; -- 2.43.0 From 0aae7f2f585a48b21dbe818abcc14058afbdae97 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Apr 2016 10:38:57 +0300 Subject: [PATCH 11/16] Adhere to the spec with connection keep-alive Recognize it case-insensitively and add the header on the response. --- source/http/server.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/http/server.cpp b/source/http/server.cpp index fbacb10..60d37c8 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "request.h" #include "response.h" #include "server.h" @@ -132,7 +133,7 @@ void Server::client_data_available(Client &cl) { cl.keepalive = false; if(cl.request->has_header("Connection")) - cl.keepalive = (cl.request->get_header("Connection")=="keep-alive"); + cl.keepalive = !strcasecmp(cl.request->get_header("Connection"), "keep-alive"); response = new Response(NONE); try @@ -168,6 +169,8 @@ void Server::client_data_available(Client &cl) void Server::send_response(Client &cl, Response &resp) { + if(cl.keepalive) + resp.set_header("Connection", "keep-alive"); cl.sock->write(resp.str()); cl.async = false; if(cl.keepalive) -- 2.43.0 From 065568e3a84b596ea15f80f67574ea7708d4e111 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Apr 2016 10:39:55 +0300 Subject: [PATCH 12/16] Provide a method for the application to cancel connection keep-alive --- source/http/server.cpp | 5 +++++ source/http/server.h | 1 + 2 files changed, 6 insertions(+) diff --git a/source/http/server.cpp b/source/http/server.cpp index 60d37c8..fc6d61f 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -66,6 +66,11 @@ void Server::submit_response(Response &resp) send_response(cl, *cl.response); } +void Server::cancel_keepalive(Response &resp) +{ + get_client_by_response(resp).keepalive = false; +} + void Server::data_available() { Net::StreamSocket *csock = sock.accept(); diff --git a/source/http/server.h b/source/http/server.h index 29b2b45..3679013 100644 --- a/source/http/server.h +++ b/source/http/server.h @@ -44,6 +44,7 @@ public: void use_event_dispatcher(IO::EventDispatcher *); void delay_response(Response &); void submit_response(Response &); + void cancel_keepalive(Response &); private: void data_available(); void client_data_available(Client &); -- 2.43.0 From afb8690a16f16890c322bdaf752574e4428a9c02 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 21 Apr 2016 00:00:21 +0300 Subject: [PATCH 13/16] Add the most common redirection statuses --- source/http/status.cpp | 2 ++ source/http/status.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/source/http/status.cpp b/source/http/status.cpp index c5fdf96..b1949ec 100644 --- a/source/http/status.cpp +++ b/source/http/status.cpp @@ -11,6 +11,8 @@ ostream &operator<<(ostream &out, Status status) { case NONE: out<<"None"; break; case OK: out<<"OK"; break; + case MOVED_PERMANENTLY: out<<"Moved Permanently"; break; + case SEE_OTHER: out<<"See Other"; break; case BAD_REQUEST: out<<"Bad Request"; break; case FORBIDDEN: out<<"Forbidden"; break; case NOT_FOUND: out<<"Not Found"; break; diff --git a/source/http/status.h b/source/http/status.h index fe18a85..5e7d54b 100644 --- a/source/http/status.h +++ b/source/http/status.h @@ -10,6 +10,8 @@ enum Status { NONE = 0, OK = 200, + MOVED_PERMANENTLY = 301, + SEE_OTHER = 303, BAD_REQUEST = 400, FORBIDDEN = 403, NOT_FOUND = 404, -- 2.43.0 From 451c140747bef1829d55d20a33dd3543b9ab8c98 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 16 May 2016 12:06:19 +0300 Subject: [PATCH 14/16] Add a utility struct for parsing complex header values It's not used to store headers in Message since not all headers follow the same syntax. User-Agent in particular may have semicolons which are not parameters separators. --- source/http/header.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++ source/http/header.h | 35 ++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 source/http/header.cpp create mode 100644 source/http/header.h diff --git a/source/http/header.cpp b/source/http/header.cpp new file mode 100644 index 0000000..02c82ca --- /dev/null +++ b/source/http/header.cpp @@ -0,0 +1,82 @@ +#include +#include +#include "header.h" +#include "message.h" + +using namespace std; + +namespace Msp { +namespace Http { + +Header::Header(const Message &msg, const string &n): + name(n), + raw_value(msg.get_header(name)) +{ + parse(); +} + +Header::Header(const string &n, const string &rv): + name(n), + raw_value(rv) +{ + parse(); +} + +void Header::parse() +{ + string::const_iterator i = raw_value.begin(); + while(i!=raw_value.end()) + { + Value value; + + string::const_iterator start = i; + for(; (i!=raw_value.end() && *i!=';' && *i!=','); ++i) ; + value.value = strip(string(start, i)); + if(value.value.empty()) + throw invalid_argument("Header::parse"); + + while(i!=raw_value.end() && *i!=',') + { + start = ++i; + for(; (i!=raw_value.end() && *i!=';' && *i!=',' && *i!='='); ++i) ; + string pname = strip(string(start, i)); + if(pname.empty()) + throw invalid_argument("Header::parse"); + + string pvalue; + if(i!=raw_value.end() && *i=='=') + { + for(++i; (i!=raw_value.end() && isspace(*i)); ++i) ; + if(i==raw_value.end() || *i==';' || *i==',') + throw invalid_argument("Header::parse"); + + if(*i=='"') + { + start = ++i; + for(; (i!=raw_value.end() && *i!='"'); ++i) ; + if(i==raw_value.end()) + throw invalid_argument("Header::parse"); + + pvalue = string(start, i); + + for(++i; (i!=raw_value.end() && *i!=';' && *i!=','); ++i) + if(!isspace(*i)) + throw invalid_argument("Header::parse"); + } + else + { + start = i; + for(; (i!=raw_value.end() && *i!=';' && *i!=','); ++i) ; + pvalue = strip(string(start, i)); + } + } + + value.parameters[pname] = pvalue; + } + + values.push_back(value); + } +} + +} // namespace Http +} // namespace Msp diff --git a/source/http/header.h b/source/http/header.h new file mode 100644 index 0000000..202b844 --- /dev/null +++ b/source/http/header.h @@ -0,0 +1,35 @@ +#ifndef MSP_HTTP_HEADER_H_ +#define MSP_HTTP_HEADER_H_ + +#include +#include +#include + +namespace Msp { +namespace Http { + +class Message; + +struct Header +{ + struct Value + { + std::string value; + std::map parameters; + }; + + std::string name; + std::string raw_value; + std::vector values; + + Header() { } + Header(const Message &, const std::string &); + Header(const std::string &, const std::string &); + + void parse(); +}; + +} // namespace Http +} // namespace Msp + +#endif -- 2.43.0 From 049fc42b671d623815408c3658a450b73063fec5 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 16 May 2016 18:13:38 +0300 Subject: [PATCH 15/16] Add a class for parsing submitted form data parse_query in utils.h already did it for query strings, but not for POST requests. --- source/http/formdata.cpp | 97 ++++++++++++++++++++++++++++++++++++++ source/http/formdata.h | 29 ++++++++++++ source/http/submessage.cpp | 24 ++++++++++ source/http/submessage.h | 23 +++++++++ 4 files changed, 173 insertions(+) create mode 100644 source/http/formdata.cpp create mode 100644 source/http/formdata.h create mode 100644 source/http/submessage.cpp create mode 100644 source/http/submessage.h diff --git a/source/http/formdata.cpp b/source/http/formdata.cpp new file mode 100644 index 0000000..182c5bb --- /dev/null +++ b/source/http/formdata.cpp @@ -0,0 +1,97 @@ +#include +#include "header.h" +#include "request.h" +#include "submessage.h" +#include "utils.h" +#include "formdata.h" + +using namespace std; + +namespace Msp { +namespace Http { + +FormData::FormData(const Request &req) +{ + const string &method = req.get_method(); + if(method=="GET") + { + Url url = parse_url(req.get_path()); + fields = parse_query(url.query); + } + else if(method=="POST") + { + Header content_type(req, "Content-Type"); + const Header::Value &ct_value = content_type.values.at(0); + if(ct_value.value=="application/x-www-form-urlencoded") + fields = parse_query(req.get_content()); + else if(ct_value.value=="multipart/form-data") + { + const string &boundary = get_item(ct_value.parameters, "boundary"); + parse_multipart(req, boundary); + } + else + throw invalid_argument("FormData::FormData"); + } + else + throw invalid_argument("FormData::FormData"); +} + +void FormData::parse_multipart(const Request &req, const string &boundary) +{ + const string &content = req.get_content(); + + string::size_type line_start = 0; + string::size_type part_start = 0; + while(1) + { + string::size_type lf = content.find('\n', line_start); + if(lf==string::npos) + throw invalid_argument("FormData::parse_multipart"); + + bool is_boundary = !content.compare(line_start, 2, "--"); + is_boundary = (is_boundary && !content.compare(line_start+2, boundary.size(), boundary)); + + if(is_boundary) + { + /* The CRLF preceding the boundary delimiter is treated as part + of the delimiter as per RFC 2046 */ + string::size_type part_end = line_start-1; + if(content[part_end-1]=='\r') + --part_end; + + if(part_start>0) + { + SubMessage part = SubMessage::parse(content.substr(part_start, line_start-part_start)); + Header content_disposition(part, "Content-Disposition"); + const Header::Value &cd_value = content_disposition.values.at(0); + if(cd_value.value=="form-data") + { + const string &name = get_item(cd_value.parameters, "name"); + fields[name] = part.get_content(); + } + } + + part_start = lf+1; + } + + if(!content.compare(line_start+2+boundary.size(), 2, "--")) + break; + + line_start = lf+1; + } +} + +const string &FormData::get_value(const string &key) const +{ + map::const_iterator i = fields.find(key); + if(i==fields.end()) + { + static string dummy; + return dummy; + } + + return i->second; +} + +} // namespace Http +} // namespace Msp diff --git a/source/http/formdata.h b/source/http/formdata.h new file mode 100644 index 0000000..3232891 --- /dev/null +++ b/source/http/formdata.h @@ -0,0 +1,29 @@ +#ifndef MSP_HTTP_FORMDATA_H_ +#define MSP_HTTP_FORMDATA_H_ + +#include +#include + +namespace Msp { +namespace Http { + +class Request; + +class FormData +{ +private: + std::map fields; + +public: + FormData(const Request &); +private: + void parse_multipart(const Request &, const std::string &); + +public: + const std::string &get_value(const std::string &) const; +}; + +} // namespace Http +} // namespace Msp + +#endif diff --git a/source/http/submessage.cpp b/source/http/submessage.cpp new file mode 100644 index 0000000..05b1a30 --- /dev/null +++ b/source/http/submessage.cpp @@ -0,0 +1,24 @@ +#include "submessage.h" + +using namespace std; + +namespace Msp { +namespace Http { + +string SubMessage::str() const +{ + return str_common(); +} + +SubMessage SubMessage::parse(const string &str) +{ + SubMessage result; + + string::size_type pos = result.parse_headers(str); + result.content = str.substr(pos); + + return result; +} + +} // namespace Http +} // namespace Msp diff --git a/source/http/submessage.h b/source/http/submessage.h new file mode 100644 index 0000000..f60276a --- /dev/null +++ b/source/http/submessage.h @@ -0,0 +1,23 @@ +#ifndef MSP_HTTP_SUBMESSAGE_H_ +#define MSP_HTTP_SUBMESSAGE_H_ + +#include "message.h" + +namespace Msp { +namespace Http { + +class SubMessage: public Message +{ +private: + SubMessage() { } + +public: + virtual std::string str() const; + + static SubMessage parse(const std::string &); +}; + +} // namespace Http +} // namespace Msp + +#endif -- 2.43.0 From b4e0f7ed23f24e78fd09c9b7f206279e16d38c1e Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 16 May 2016 18:15:10 +0300 Subject: [PATCH 16/16] Use string::size_type to store string offsets --- source/http/message.cpp | 4 ++-- source/http/request.cpp | 2 +- source/http/response.cpp | 2 +- source/http/server.cpp | 2 +- source/net/resolve.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/http/message.cpp b/source/http/message.cpp index 21d2c4b..92321e8 100644 --- a/source/http/message.cpp +++ b/source/http/message.cpp @@ -97,7 +97,7 @@ unsigned Message::parse_content(const string &d) unsigned Message::parse_headers(const string &d) { - unsigned start = 0; + string::size_type start = 0; while(1) { string::size_type lf = d.find('\n', start); @@ -106,7 +106,7 @@ unsigned Message::parse_headers(const string &d) if(lf==start || (d[start]=='\r' && lf==start+1)) return lf+1; - unsigned colon = d.find(':', start); + string::size_type colon = d.find(':', start); if(colon>lf) throw invalid_argument("Message::parse_headers"); diff --git a/source/http/request.cpp b/source/http/request.cpp index f050e04..54b0bee 100644 --- a/source/http/request.cpp +++ b/source/http/request.cpp @@ -27,7 +27,7 @@ string Request::str() const Request Request::parse(const string &str) { - unsigned lf = str.find('\n'); + string::size_type lf = str.find('\n'); vector parts = split(str.substr(0, lf-(str[lf-1]=='\r')), ' ', 2); if(parts.size()<3) throw invalid_argument("Request::parse"); diff --git a/source/http/response.cpp b/source/http/response.cpp index 85e279f..739a20f 100644 --- a/source/http/response.cpp +++ b/source/http/response.cpp @@ -23,7 +23,7 @@ Response Response::parse(const string &str) { Response result; - unsigned lf = str.find('\n'); + string::size_type lf = str.find('\n'); vector parts = split(str.substr(0, lf), ' ', 2); if(parts.size()<2) throw invalid_argument("Response::parse"); diff --git a/source/http/server.cpp b/source/http/server.cpp index fc6d61f..f0474fd 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -106,7 +106,7 @@ void Server::client_data_available(Client &cl) cl.request = new Request(Request::parse(cl.in_buf)); string addr_str = cl.sock->get_peer_address().str(); - unsigned colon = addr_str.find(':'); + string::size_type colon = addr_str.find(':'); cl.request->set_header("-Client-Host", addr_str.substr(0, colon)); if(cl.request->get_method()!="GET" && cl.request->get_method()!="POST") diff --git a/source/net/resolve.cpp b/source/net/resolve.cpp index 097fea3..88497fa 100644 --- a/source/net/resolve.cpp +++ b/source/net/resolve.cpp @@ -54,7 +54,7 @@ SockAddr *resolve(const string &str, Family family) string host, serv; if(str[0]=='[') { - unsigned bracket = str.find(']'); + string::size_type bracket = str.find(']'); host = str.substr(1, bracket-1); string::size_type colon = str.find(':', bracket); if(colon!=string::npos) -- 2.43.0