]> git.tdb.fi Git - libs/net.git/commitdiff
Prepare for assimilation into mspnet http-master
authorMikko Rasa <tdb@tdb.fi>
Wed, 10 Aug 2011 18:07:48 +0000 (21:07 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 10 Aug 2011 18:07:48 +0000 (21:07 +0300)
32 files changed:
source/client.cpp [deleted file]
source/client.h [deleted file]
source/http/client.cpp [new file with mode: 0644]
source/http/client.h [new file with mode: 0644]
source/http/message.cpp [new file with mode: 0644]
source/http/message.h [new file with mode: 0644]
source/http/request.cpp [new file with mode: 0644]
source/http/request.h [new file with mode: 0644]
source/http/response.cpp [new file with mode: 0644]
source/http/response.h [new file with mode: 0644]
source/http/server.cpp [new file with mode: 0644]
source/http/server.h [new file with mode: 0644]
source/http/status.cpp [new file with mode: 0644]
source/http/status.h [new file with mode: 0644]
source/http/utils.cpp [new file with mode: 0644]
source/http/utils.h [new file with mode: 0644]
source/http/version.cpp [new file with mode: 0644]
source/http/version.h [new file with mode: 0644]
source/message.cpp [deleted file]
source/message.h [deleted file]
source/request.cpp [deleted file]
source/request.h [deleted file]
source/response.cpp [deleted file]
source/response.h [deleted file]
source/server.cpp [deleted file]
source/server.h [deleted file]
source/status.cpp [deleted file]
source/status.h [deleted file]
source/utils.cpp [deleted file]
source/utils.h [deleted file]
source/version.cpp [deleted file]
source/version.h [deleted file]

diff --git a/source/client.cpp b/source/client.cpp
deleted file mode 100644 (file)
index 0729544..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-#include <msp/core/except.h>
-#include <msp/core/refptr.h>
-#include <msp/net/resolve.h>
-#include <msp/time/units.h>
-#include "client.h"
-#include "request.h"
-#include "response.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-Client::Client():
-       sock(0),
-       event_disp(0),
-       user_agent("libmsphttp/0.1"),
-       request(0),
-       response(0)
-{ }
-
-Client::~Client()
-{
-       delete sock;
-       delete request;
-       delete response;
-}
-
-void Client::use_event_dispatcher(IO::EventDispatcher *ed)
-{
-       if(event_disp && sock)
-               event_disp->remove(*sock);
-       event_disp = ed;
-       if(event_disp && sock)
-               event_disp->add(*sock);
-}
-
-void Client::start_request(const Request &r)
-{
-       if(request)
-               throw InvalidState("Already processing a request");
-
-       string host = r.get_header("Host");
-       if(host.find(':')==string::npos)
-               host += ":80";
-       RefPtr<Net::SockAddr> addr = Net::resolve(host);
-
-       delete sock;
-       sock = new Net::StreamSocket(addr->get_family());
-       sock->set_block(false);
-
-       sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available));
-       sock->signal_connect_finished.connect(sigc::mem_fun(this, &Client::connect_finished));
-       if(event_disp)
-               event_disp->add(*sock);
-
-       sock->connect(*addr);
-
-       request = new Request(r);
-       if(!user_agent.empty())
-               request->set_header("User-Agent", user_agent);
-
-       delete response;
-       response = 0;
-       in_buf.clear();
-}
-
-const Response *Client::get_url(const std::string &url)
-{
-       start_request(Request::from_url(url));
-       wait_response();
-       return response;
-}
-
-void Client::tick()
-{
-       if(!request)
-               return;
-
-       while(IO::PollEvent ev = IO::poll(*sock, sock->get_events(), Time::zero))
-               sock->event(ev);
-
-       if(response && response->is_complete())
-       {
-               signal_response_complete.emit(*response);
-
-               delete sock;
-               sock = 0;
-               delete request;
-               request = 0;
-       }
-}
-
-void Client::wait_response()
-{
-       while(request && (!response || !response->is_complete()))
-               tick();
-}
-
-void Client::abort()
-{
-       delete sock;
-       sock = 0;
-       delete request;
-       request = 0;
-}
-
-void Client::connect_finished(const exception *err)
-{
-       if(err)
-       {
-               signal_socket_error.emit(err);
-
-               delete request;
-               request = 0;
-       }
-       else
-               sock->write(request->str());
-}
-
-void Client::data_available()
-{
-       char rbuf[4096];
-       unsigned len;
-       try
-       {
-               len = sock->read(rbuf, sizeof(rbuf));
-       }
-       catch(const exception &e)
-       {
-               signal_socket_error.emit(&e);
-               return;
-       }
-
-       if(!len)
-               return;
-       in_buf.append(rbuf, len);
-
-       if(!response)
-       {
-               if(in_buf.find("\r\n\r\n")!=string::npos || in_buf.find("\n\n")!=string::npos)
-               {
-                       response = new Response(Response::parse(in_buf));
-                       response->set_user_data(request->get_user_data());
-                       in_buf = string();
-               }
-       }
-       else
-       {
-               len = response->parse_content(in_buf);
-               in_buf.erase(0, len);
-       }
-
-       if(response && response->is_complete())
-       {
-               signal_response_complete.emit(*response);
-
-               delete request;
-               request = 0;
-       }
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/client.h b/source/client.h
deleted file mode 100644 (file)
index ee10f8c..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef MSP_HTTP_CLIENT_H_
-#define MSP_HTTP_CLIENT_H_
-
-#include <string>
-#include <sigc++/signal.h>
-#include <msp/io/eventdispatcher.h>
-#include <msp/net/streamsocket.h>
-
-namespace Msp {
-namespace Http {
-
-class Request;
-class Response;
-
-class Client
-{
-public:
-       sigc::signal<void, const Response &> signal_response_complete;
-       sigc::signal<void, const std::exception *> signal_socket_error;
-
-private:
-       Net::StreamSocket *sock;
-       IO::EventDispatcher *event_disp;
-       std::string user_agent;
-       Request *request;
-       Response *response;
-       std::string in_buf;
-
-       Client(const Client &);
-       Client &operator=(const Client &);
-public:
-       Client();
-       ~Client();
-
-       void use_event_dispatcher(IO::EventDispatcher *);
-       void set_user_agent(const std::string &);
-       void start_request(const Request &);
-       const Response *get_url(const std::string &);
-       void tick();
-       void wait_response();
-       void abort();
-       const Request *get_request() const { return request; }
-       const Response *get_response() const { return response; }
-private:
-       void connect_finished(const std::exception *);
-       void data_available();
-};
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/http/client.cpp b/source/http/client.cpp
new file mode 100644 (file)
index 0000000..0729544
--- /dev/null
@@ -0,0 +1,164 @@
+#include <msp/core/except.h>
+#include <msp/core/refptr.h>
+#include <msp/net/resolve.h>
+#include <msp/time/units.h>
+#include "client.h"
+#include "request.h"
+#include "response.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+Client::Client():
+       sock(0),
+       event_disp(0),
+       user_agent("libmsphttp/0.1"),
+       request(0),
+       response(0)
+{ }
+
+Client::~Client()
+{
+       delete sock;
+       delete request;
+       delete response;
+}
+
+void Client::use_event_dispatcher(IO::EventDispatcher *ed)
+{
+       if(event_disp && sock)
+               event_disp->remove(*sock);
+       event_disp = ed;
+       if(event_disp && sock)
+               event_disp->add(*sock);
+}
+
+void Client::start_request(const Request &r)
+{
+       if(request)
+               throw InvalidState("Already processing a request");
+
+       string host = r.get_header("Host");
+       if(host.find(':')==string::npos)
+               host += ":80";
+       RefPtr<Net::SockAddr> addr = Net::resolve(host);
+
+       delete sock;
+       sock = new Net::StreamSocket(addr->get_family());
+       sock->set_block(false);
+
+       sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available));
+       sock->signal_connect_finished.connect(sigc::mem_fun(this, &Client::connect_finished));
+       if(event_disp)
+               event_disp->add(*sock);
+
+       sock->connect(*addr);
+
+       request = new Request(r);
+       if(!user_agent.empty())
+               request->set_header("User-Agent", user_agent);
+
+       delete response;
+       response = 0;
+       in_buf.clear();
+}
+
+const Response *Client::get_url(const std::string &url)
+{
+       start_request(Request::from_url(url));
+       wait_response();
+       return response;
+}
+
+void Client::tick()
+{
+       if(!request)
+               return;
+
+       while(IO::PollEvent ev = IO::poll(*sock, sock->get_events(), Time::zero))
+               sock->event(ev);
+
+       if(response && response->is_complete())
+       {
+               signal_response_complete.emit(*response);
+
+               delete sock;
+               sock = 0;
+               delete request;
+               request = 0;
+       }
+}
+
+void Client::wait_response()
+{
+       while(request && (!response || !response->is_complete()))
+               tick();
+}
+
+void Client::abort()
+{
+       delete sock;
+       sock = 0;
+       delete request;
+       request = 0;
+}
+
+void Client::connect_finished(const exception *err)
+{
+       if(err)
+       {
+               signal_socket_error.emit(err);
+
+               delete request;
+               request = 0;
+       }
+       else
+               sock->write(request->str());
+}
+
+void Client::data_available()
+{
+       char rbuf[4096];
+       unsigned len;
+       try
+       {
+               len = sock->read(rbuf, sizeof(rbuf));
+       }
+       catch(const exception &e)
+       {
+               signal_socket_error.emit(&e);
+               return;
+       }
+
+       if(!len)
+               return;
+       in_buf.append(rbuf, len);
+
+       if(!response)
+       {
+               if(in_buf.find("\r\n\r\n")!=string::npos || in_buf.find("\n\n")!=string::npos)
+               {
+                       response = new Response(Response::parse(in_buf));
+                       response->set_user_data(request->get_user_data());
+                       in_buf = string();
+               }
+       }
+       else
+       {
+               len = response->parse_content(in_buf);
+               in_buf.erase(0, len);
+       }
+
+       if(response && response->is_complete())
+       {
+               signal_response_complete.emit(*response);
+
+               delete request;
+               request = 0;
+       }
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/client.h b/source/http/client.h
new file mode 100644 (file)
index 0000000..ee10f8c
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef MSP_HTTP_CLIENT_H_
+#define MSP_HTTP_CLIENT_H_
+
+#include <string>
+#include <sigc++/signal.h>
+#include <msp/io/eventdispatcher.h>
+#include <msp/net/streamsocket.h>
+
+namespace Msp {
+namespace Http {
+
+class Request;
+class Response;
+
+class Client
+{
+public:
+       sigc::signal<void, const Response &> signal_response_complete;
+       sigc::signal<void, const std::exception *> signal_socket_error;
+
+private:
+       Net::StreamSocket *sock;
+       IO::EventDispatcher *event_disp;
+       std::string user_agent;
+       Request *request;
+       Response *response;
+       std::string in_buf;
+
+       Client(const Client &);
+       Client &operator=(const Client &);
+public:
+       Client();
+       ~Client();
+
+       void use_event_dispatcher(IO::EventDispatcher *);
+       void set_user_agent(const std::string &);
+       void start_request(const Request &);
+       const Response *get_url(const std::string &);
+       void tick();
+       void wait_response();
+       void abort();
+       const Request *get_request() const { return request; }
+       const Response *get_response() const { return response; }
+private:
+       void connect_finished(const std::exception *);
+       void data_available();
+};
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/message.cpp b/source/http/message.cpp
new file mode 100644 (file)
index 0000000..2b3e40b
--- /dev/null
@@ -0,0 +1,152 @@
+#include <cstdlib>
+#include <msp/core/maputils.h>
+#include <msp/strings/formatter.h>
+#include <msp/strings/utils.h>
+#include "message.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+Message::Message():
+       http_version(0x11),
+       chunk_length(0),
+       complete(false)
+{ }
+
+void Message::set_header(const string &hdr, const string &val)
+{
+       headers[normalize_header_name(hdr)] = val;
+}
+
+bool Message::has_header(const string &hdr) const
+{
+       return headers.count(normalize_header_name(hdr));
+}
+
+const string &Message::get_header(const string &hdr) const
+{
+       return get_item(headers, normalize_header_name(hdr));
+}
+
+void Message::add_content(const string &d)
+{
+       content += d;
+       if(headers.count("Content-Type")==0)
+               set_header("Content-Type", "text/plain");
+       set_header("Content-Length", lexical_cast(content.size()));
+}
+
+void Message::set_user_data(const Variant &d)
+{
+       user_data = d;
+}
+
+unsigned Message::parse_content(const string &d)
+{
+       if(complete)
+               return 0;
+
+       HeaderMap::const_iterator i = headers.find("Content-Length");
+       if(i!=headers.end())
+       {
+               unsigned needed = lexical_cast<unsigned>(i->second)-content.size();
+               unsigned len = min(needed, d.size());
+               
+               content.append(d, 0, len);
+
+               if(len==needed)
+                       complete = true;
+               
+               return len;
+       }
+
+       i = headers.find("Transfer-Encoding");
+       if(i!=headers.end() && strcasecmp(i->second, "chunked")==0)
+       {
+               unsigned pos = 0;
+               while(!complete && pos<d.size())
+               {
+                       if(chunk_length==0)
+                       {
+                               unsigned lf = d.find('\n', pos);
+                               if(lf==string::npos)
+                                       return pos;
+                               chunk_length = lexical_cast<unsigned>(strip(d.substr(pos, lf-pos)), "x");
+                               if(chunk_length==0)
+                                       complete = true;
+                               pos = lf+1;
+                       }
+                       else
+                       {
+                               unsigned len = min(chunk_length, d.size()-pos);
+                               content.append(d, pos, len);
+                               chunk_length -= len;
+                               if((pos = d.find('\n', pos+len))!=string::npos)
+                                       ++pos;
+                       }
+               }
+
+               return pos;
+       }
+
+       complete = true;
+       return 0;
+}
+
+unsigned Message::parse_headers(const string &d)
+{
+       unsigned start = 0;
+       while(1)
+       {
+               unsigned lf = d.find('\n', start);
+               if(lf==string::npos)
+                       throw invalid_argument("Message::parse_headers");
+               if(lf==start || (d[start]=='\r' && lf==start+1))
+                       return lf+1;
+
+               unsigned colon = d.find(':', start);
+               if(colon>lf)
+                       throw invalid_argument("Message::parse_headers");
+
+               set_header(d.substr(start, colon-start), strip(d.substr(colon+1, lf-colon-1)));
+
+               start = lf+1;
+       }
+}
+
+string Message::str_common() const
+{
+       string result;
+
+       for(HeaderMap::const_iterator i=headers.begin(); i!=headers.end(); ++i)
+               if(i->first[0]!='-')
+                       result += format("%s: %s\r\n", i->first, i->second);
+       result += "\r\n";
+       result += content;
+
+       return result;
+}
+
+string Message::normalize_header_name(const string &hdr) const
+{
+       string result = hdr;
+       bool upper = true;
+       for(string::iterator i=result.begin(); i!=result.end(); ++i)
+       {
+               if(upper)
+               {
+                       *i = toupper(*i);
+                       upper = false;
+               }
+               else if(*i=='-')
+                       upper = true;
+               else
+                       *i = tolower(*i);
+       }
+       return result;
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/message.h b/source/http/message.h
new file mode 100644 (file)
index 0000000..4d23102
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef MSP_HTTP_MESSAGE_H_
+#define MSP_HTTP_MESSAGE_H_
+
+#include <map>
+#include <string>
+#include <msp/core/variant.h>
+#include "version.h"
+
+namespace Msp {
+namespace Http {
+
+class Message
+{
+protected:
+       typedef std::map<std::string, std::string> HeaderMap;
+
+       Version http_version;
+       HeaderMap headers;
+       std::string content;
+       unsigned chunk_length;
+       bool complete;
+       Variant user_data;
+
+       Message();
+public:
+       virtual ~Message() { }
+
+       void set_header(const std::string &, const std::string &);
+       bool has_header(const std::string &) const;
+       const std::string &get_header(const std::string &) const;
+       void add_content(const std::string &);
+       const std::string &get_content() const { return content; }
+       void set_user_data(const Variant &);
+       const Variant &get_user_data() const { return user_data; }
+       bool is_complete() const { return complete; }
+       unsigned parse_content(const std::string &);
+       virtual std::string str() const = 0;
+protected:
+       unsigned parse_headers(const std::string &);
+       std::string str_common() const;
+       std::string normalize_header_name(const std::string &) const;
+};
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/request.cpp b/source/http/request.cpp
new file mode 100644 (file)
index 0000000..c948755
--- /dev/null
@@ -0,0 +1,65 @@
+#include <msp/strings/formatter.h>
+#include <msp/strings/regex.h>
+#include <msp/strings/utils.h>
+#include "request.h"
+#include "utils.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+Request::Request(const string &m, const string &p):
+       method(m),
+       path(p)
+{ }
+
+string Request::str() const
+{
+       string result = format("%s %s %s\r\n", method, path, version_str(http_version));
+       result += str_common();
+
+       return result;
+}
+
+Request Request::parse(const string &str)
+{
+       unsigned lf = str.find('\n');
+       vector<string> parts = split(str.substr(0, lf-(str[lf-1]=='\r')), ' ', 2);
+       if(parts.size()<3)
+               throw invalid_argument("Request::parse");
+
+       Request result(parts[0], parts[1]);
+       result.http_version = parse_version(parts[2]);
+
+       lf += result.parse_headers(str.substr(lf+1));
+
+       result.parse_content(str.substr(lf+1));
+
+       return result;
+}
+
+Request Request::from_url(const string &str)
+{
+       Url url = parse_url(str);
+       if(url.scheme!="http")
+               throw invalid_argument("Request::from_url");
+
+       string path = url.path;
+       if(path.empty())
+               path = "/";
+       if(!url.query.empty())
+       {
+               path += '?';
+               path += url.query;
+       }
+
+       Request result("GET", path);
+       result.set_header("Host", url.host);
+       result.set_header("Connection", "close");
+
+       return result;
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/request.h b/source/http/request.h
new file mode 100644 (file)
index 0000000..ec5030e
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef MSP_HTTP_REQUEST_H_
+#define MSP_HTTP_REQUEST_H_
+
+#include <string>
+#include "message.h"
+
+namespace Msp {
+namespace Http {
+
+class Request: public Message
+{
+private:
+       std::string method;
+       std::string path;
+
+public:
+       Request(const std::string &, const std::string &);
+       const std::string &get_method() const { return method; }
+       const std::string &get_path() const { return path; }
+       virtual std::string str() const;
+
+       static Request parse(const std::string &);
+       static Request from_url(const std::string &);
+};
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/response.cpp b/source/http/response.cpp
new file mode 100644 (file)
index 0000000..ede249b
--- /dev/null
@@ -0,0 +1,42 @@
+#include <msp/strings/formatter.h>
+#include <msp/strings/utils.h>
+#include "response.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+Response::Response(Status s):
+       status(s)
+{ }
+
+string Response::str() const
+{
+       string result = format("%s %d %s\r\n", version_str(http_version), static_cast<int>(status), status);
+       result += str_common();
+
+       return result;
+}
+
+Response Response::parse(const string &str)
+{
+       Response result;
+
+       unsigned lf = str.find('\n');
+       vector<string> parts = split(str.substr(0, lf), ' ', 2);
+       if(parts.size()<2)
+               throw invalid_argument("Response::parse");
+
+       result.http_version = parse_version(parts[0]);
+       result.status = static_cast<Status>(lexical_cast<unsigned>(parts[1]));
+
+       lf += result.parse_headers(str.substr(lf+1));
+
+       result.parse_content(str.substr(lf+1));
+
+       return result;
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/response.h b/source/http/response.h
new file mode 100644 (file)
index 0000000..d721ad8
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MSP_HTTP_RESPONSE_H_
+#define MSP_HTTO_RESPONSE_H_
+
+#include "message.h"
+#include "status.h"
+
+namespace Msp {
+namespace Http {
+
+class Response: public Message
+{
+private:
+       Status status;
+
+       Response() { }
+public:
+       Response(Status);
+       Status get_status() const { return status; }
+       virtual std::string str() const;
+
+       static Response parse(const std::string &);
+};
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/server.cpp b/source/http/server.cpp
new file mode 100644 (file)
index 0000000..9205cc1
--- /dev/null
@@ -0,0 +1,186 @@
+#include <exception>
+#include <msp/core/refptr.h>
+#include <msp/net/inet.h>
+#include <msp/net/resolve.h>
+#include <msp/net/streamsocket.h>
+#include <msp/strings/format.h>
+#include "request.h"
+#include "response.h"
+#include "server.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+Server::Server(unsigned port):
+       sock(Net::INET),
+       event_disp(0)
+{
+       sock.signal_data_available.connect(sigc::mem_fun(this, &Server::data_available));
+       RefPtr<Net::SockAddr> addr = Net::resolve("*", format("%d", port));
+       sock.listen(*addr, 8);
+}
+
+unsigned Server::get_port() const
+{
+       const Net::SockAddr &addr = sock.get_local_address();
+       if(addr.get_family()==Net::INET)
+               return static_cast<const Net::InetAddr &>(addr).get_port();
+       return 0;
+}
+
+void Server::use_event_dispatcher(IO::EventDispatcher *ed)
+{
+       if(event_disp)
+       {
+               event_disp->remove(sock);
+               for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
+                       event_disp->remove(*i->sock);
+       }
+       event_disp = ed;
+       if(event_disp)
+       {
+               event_disp->add(sock);
+               for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
+                       event_disp->add(*i->sock);
+       }
+}
+
+void Server::delay_response(Response &resp)
+{
+       get_client_by_response(resp).async = true;
+}
+
+void Server::submit_response(Response &resp)
+{
+       Client &cl = get_client_by_response(resp);
+       if(cl.async)
+       {
+               cl.sock->write(resp.str());
+               cl.stale = true;
+       }
+}
+
+void Server::data_available()
+{
+       Net::StreamSocket *csock = sock.accept();
+       clients.push_back(Client(csock));
+       csock->signal_data_available.connect(sigc::bind(sigc::mem_fun(this, &Server::client_data_available), sigc::ref(clients.back())));
+       csock->signal_end_of_file.connect(sigc::bind(sigc::mem_fun(this, &Server::client_end_of_file), sigc::ref(clients.back())));
+       if(event_disp)
+               event_disp->add(*csock);
+}
+
+void Server::client_data_available(Client &cl)
+{
+       for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
+               if(i->stale && &*i!=&cl)
+               {
+                       clients.erase(i);
+                       break;
+               }
+
+       char rbuf[4096];
+       unsigned len = cl.sock->read(rbuf, sizeof(rbuf));
+       cl.in_buf.append(rbuf, len);
+
+       RefPtr<Response> response;
+       if(!cl.request)
+       {
+               if(cl.in_buf.find("\r\n\r\n")!=string::npos || cl.in_buf.find("\n\n")!=string::npos)
+               {
+                       try
+                       {
+                               cl.request = new Request(Request::parse(cl.in_buf));
+
+                               string addr_str = cl.sock->get_peer_address().str();
+                               unsigned 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")
+                               {
+                                       response = new Response(NOT_IMPLEMENTED);
+                                       response->add_content("Method not implemented\n");
+                               }
+                       }
+                       catch(const exception &e)
+                       {
+                               response = new Response(BAD_REQUEST);
+                               response->add_content(e.what());
+                       }
+                       cl.in_buf = string();
+               }
+       }
+       else
+       {
+               len = cl.request->parse_content(cl.in_buf);
+               cl.in_buf.erase(0, len);
+       }
+
+       if(cl.request && cl.request->is_complete() && !response)
+       {
+               response = new Response(NONE);
+               try
+               {
+                       cl.response = response.get();
+                       signal_request.emit(*cl.request, *response);
+                       if(cl.async)
+                               response.release();
+                       else
+                       {
+                               cl.response = 0;
+                               if(response->get_status()==NONE)
+                               {
+                                       response = new Response(NOT_FOUND);
+                                       response->add_content("The requested resource was not found\n");
+                               }
+                       }
+               }
+               catch(const exception &e)
+               {
+                       cl.response = 0;
+                       response = new Response(INTERNAL_ERROR);
+                       response->add_content(e.what());
+               }
+       }
+
+       if(response)
+       {
+               cl.sock->write(response->str());
+               cl.stale = true;
+       }
+}
+
+void Server::client_end_of_file(Client &cl)
+{
+       cl.stale = true;
+}
+
+Server::Client &Server::get_client_by_response(Response &resp)
+{
+       for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
+               if(i->response==&resp)
+                       return *i;
+
+       // XXX Do this differently
+       throw invalid_argument("Response does not belong to any client");
+}
+
+
+Server::Client::Client(RefPtr<Net::StreamSocket> s):
+       sock(s),
+       request(0),
+       response(0),
+       async(false),
+       stale(false)
+{ }
+
+Server::Client::~Client()
+{
+       delete request;
+       delete response;
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/server.h b/source/http/server.h
new file mode 100644 (file)
index 0000000..41894db
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef MSP_HTTP_SERVER_H_
+#define MSP_HTTP_SERVER_H_
+
+#include <msp/core/refptr.h>
+#include <msp/io/eventdispatcher.h>
+#include <msp/net/streamserversocket.h>
+
+namespace Msp {
+namespace Http {
+
+class Request;
+class Response;
+
+class Server
+{
+public:
+       sigc::signal<void, const Request &, Response &> signal_request;
+
+private:
+       struct Client
+       {
+               RefPtr<Net::StreamSocket> sock;
+               std::string in_buf;
+               Request *request;
+               Response *response;
+               bool async;
+               bool stale;
+
+               Client(RefPtr<Net::StreamSocket>);
+               ~Client();
+       };
+
+       Net::StreamServerSocket sock;
+       std::list<Client> clients;
+       IO::EventDispatcher *event_disp;
+
+public:
+       Server(unsigned);
+       unsigned get_port() const;
+       void use_event_dispatcher(IO::EventDispatcher *);
+       void delay_response(Response &);
+       void submit_response(Response &);
+private:
+       void data_available();
+       void client_data_available(Client &);
+       void client_end_of_file(Client &);
+       Client &get_client_by_response(Response &);
+};
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/status.cpp b/source/http/status.cpp
new file mode 100644 (file)
index 0000000..6b95822
--- /dev/null
@@ -0,0 +1,26 @@
+#include "status.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+ostream &operator<<(ostream &out, Status status)
+{
+       switch(status)
+       {
+       case NONE: out<<"None"; break;
+       case OK: out<<"OK"; break;
+       case BAD_REQUEST: out<<"Bad Request"; break;
+       case FORBIDDEN: out<<"Forbidden"; break;
+       case NOT_FOUND: out<<"Not Found"; break;
+       case INTERNAL_ERROR: out<<"Internal Error"; break;
+       case NOT_IMPLEMENTED: out<<"Not Implemented"; break;
+       default: out<<"Unknown Status"; break;
+       }
+
+       return out;
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/status.h b/source/http/status.h
new file mode 100644 (file)
index 0000000..075016d
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef MSP_HTTPSERVER_STATUS_H_
+#define MSP_HTTPSERVER_STATUS_H_
+
+#include <ostream>
+
+namespace Msp {
+namespace Http {
+
+enum Status
+{
+       NONE = 0,
+       OK = 200,
+       BAD_REQUEST = 400,
+       FORBIDDEN = 403,
+       NOT_FOUND = 404,
+       INTERNAL_ERROR = 500,
+       NOT_IMPLEMENTED = 501
+};
+
+extern std::ostream &operator<<(std::ostream &, Status);
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/utils.cpp b/source/http/utils.cpp
new file mode 100644 (file)
index 0000000..5a5dc04
--- /dev/null
@@ -0,0 +1,149 @@
+#include <algorithm>
+#include <msp/strings/formatter.h>
+#include <msp/strings/regex.h>
+#include <msp/strings/utils.h>
+#include "utils.h"
+
+using namespace std;
+
+namespace {
+
+const char *reserved[]=
+{
+       " #%&+=?",
+       " #%&*+:;=?@[]",
+       " !#$%&'()*+,/:;=?@[]",
+};
+
+bool is_reserved(char c, unsigned level)
+{
+       for(const char *r=reserved[level]; *r; ++r)
+               if(c==*r)
+                       return true;
+       return false;
+}
+
+}
+
+namespace Msp {
+namespace Http {
+
+string urlencode(const string &str, EncodeLevel level)
+{
+       string result;
+       for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
+       {
+               if(is_reserved(*i, level))
+                       result += format("%%%02X", *i);
+               else
+                       result += *i;
+       }
+       return result;
+}
+
+string urlencode_plus(const string &str, EncodeLevel level)
+{
+       string result;
+       for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
+       {
+               if(*i==' ')
+                       result += '+';
+               else if(is_reserved(*i, level))
+                       result += format("%%%02X", *i);
+               else
+                       result += *i;
+       }
+       return result;
+}
+
+string urldecode(const string &str)
+{
+       string result;
+       for(unsigned i=0; i<str.size(); ++i)
+       {
+               char c = str[i];
+               if(c=='%')
+               {
+                       if(i+3>str.size())
+                               throw invalid_argument("urldecode");
+                       result += lexical_cast<unsigned char>(str.substr(i+1, 2), "x");
+                       i += 2;
+               }
+               else if(c=='+')
+                       result += ' ';
+               else
+                       result += c;
+       }
+       return result;
+}
+
+Url parse_url(const string &str)
+{
+       static Regex r_url("(([a-z]+)://)?([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*(:[0-9])?)?(/[^?#]*)?(\\?([^#]+))?(#(.*))?");
+       if(RegMatch m = r_url.match(str))
+       {
+               Url url;
+               url.scheme = m[2].str;
+               url.host = m[3].str;
+               url.path = urldecode(m[6].str);
+               url.query = m[8].str;
+               url.fragment = m[10].str;
+               return url;
+       }
+       else
+               throw invalid_argument("parse_url");
+}
+
+string build_url(const Url &url)
+{
+       if(!url.path.empty() && url.path[0]!='/')
+               throw invalid_argument("build_url");
+
+       string str;
+       if(!url.scheme.empty())
+               str += url.scheme+"://";
+       str += url.host;
+       str += urlencode(url.path);
+       if(!url.query.empty())
+       {
+               str += '?';
+               str += url.query;
+       }
+       if(!url.fragment.empty())
+       {
+               str += '#';
+               str += url.fragment;
+       }
+       return str;
+}
+
+Query parse_query(const std::string &str)
+{
+       vector<string> parts = split(str, '&');
+       Query query;
+       for(vector<string>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+       {
+               unsigned equals = i->find('=');
+               string &value = query[urldecode(i->substr(0, equals))];
+               if(equals!=string::npos)
+                       value = urldecode(i->substr(equals+1));
+       }
+       return query;
+}
+
+string build_query(const Query &query)
+{
+       string str;
+       for(Query::const_iterator i=query.begin(); i!=query.end(); ++i)
+       {
+               if(i!=query.begin())
+                       str += '&';
+               str += urlencode_plus(i->first);
+               str += '=';
+               str += urlencode_plus(i->second);
+       }
+       return str;
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/utils.h b/source/http/utils.h
new file mode 100644 (file)
index 0000000..a25a748
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef MSP_HTTP_UTILS_H_
+#define MSP_HTTP_UTILS_H_
+
+#include <map>
+#include <string>
+
+namespace Msp {
+namespace Http {
+
+enum EncodeLevel
+{
+       MINIMAL,
+       SAFE,
+       PARANOID
+};
+
+struct Url
+{
+       std::string scheme;
+       std::string host;
+       std::string path;
+       std::string query;
+       std::string fragment;
+};
+
+typedef std::map<std::string, std::string> Query;
+
+std::string urlencode(const std::string &, EncodeLevel =SAFE);
+std::string urlencode_plus(const std::string &, EncodeLevel =SAFE);
+std::string urldecode(const std::string &);
+Url parse_url(const std::string &);
+std::string build_url(const Url &);
+Query parse_query(const std::string &);
+std::string build_query(const Query &);
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/http/version.cpp b/source/http/version.cpp
new file mode 100644 (file)
index 0000000..f22b46e
--- /dev/null
@@ -0,0 +1,25 @@
+#include <msp/strings/formatter.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/regex.h>
+#include "version.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Http {
+
+Version parse_version(const std::string &ver)
+{
+       if(RegMatch match = Regex("^HTTP/([0-9]+).([0-9]+)$").match(ver))
+               return lexical_cast<unsigned>(match[1].str)<<4 | lexical_cast<unsigned>(match[2].str);
+       else
+               throw invalid_argument("parse_version");
+}
+
+string version_str(Version ver)
+{
+       return format("HTTP/%u.%u", (ver>>4)&0xF, ver&0xF);
+}
+
+} // namespace Http
+} // namespace Msp
diff --git a/source/http/version.h b/source/http/version.h
new file mode 100644 (file)
index 0000000..19a50b9
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_HTTP_MISC_H_
+#define MSP_HTTP_MISC_H_
+
+#include <string>
+
+namespace Msp {
+namespace Http {
+
+typedef unsigned Version;
+
+Version parse_version(const std::string &);
+std::string version_str(Version);
+
+} // namespace Http
+} // namespace Msp
+
+#endif
diff --git a/source/message.cpp b/source/message.cpp
deleted file mode 100644 (file)
index 2b3e40b..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-#include <cstdlib>
-#include <msp/core/maputils.h>
-#include <msp/strings/formatter.h>
-#include <msp/strings/utils.h>
-#include "message.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-Message::Message():
-       http_version(0x11),
-       chunk_length(0),
-       complete(false)
-{ }
-
-void Message::set_header(const string &hdr, const string &val)
-{
-       headers[normalize_header_name(hdr)] = val;
-}
-
-bool Message::has_header(const string &hdr) const
-{
-       return headers.count(normalize_header_name(hdr));
-}
-
-const string &Message::get_header(const string &hdr) const
-{
-       return get_item(headers, normalize_header_name(hdr));
-}
-
-void Message::add_content(const string &d)
-{
-       content += d;
-       if(headers.count("Content-Type")==0)
-               set_header("Content-Type", "text/plain");
-       set_header("Content-Length", lexical_cast(content.size()));
-}
-
-void Message::set_user_data(const Variant &d)
-{
-       user_data = d;
-}
-
-unsigned Message::parse_content(const string &d)
-{
-       if(complete)
-               return 0;
-
-       HeaderMap::const_iterator i = headers.find("Content-Length");
-       if(i!=headers.end())
-       {
-               unsigned needed = lexical_cast<unsigned>(i->second)-content.size();
-               unsigned len = min(needed, d.size());
-               
-               content.append(d, 0, len);
-
-               if(len==needed)
-                       complete = true;
-               
-               return len;
-       }
-
-       i = headers.find("Transfer-Encoding");
-       if(i!=headers.end() && strcasecmp(i->second, "chunked")==0)
-       {
-               unsigned pos = 0;
-               while(!complete && pos<d.size())
-               {
-                       if(chunk_length==0)
-                       {
-                               unsigned lf = d.find('\n', pos);
-                               if(lf==string::npos)
-                                       return pos;
-                               chunk_length = lexical_cast<unsigned>(strip(d.substr(pos, lf-pos)), "x");
-                               if(chunk_length==0)
-                                       complete = true;
-                               pos = lf+1;
-                       }
-                       else
-                       {
-                               unsigned len = min(chunk_length, d.size()-pos);
-                               content.append(d, pos, len);
-                               chunk_length -= len;
-                               if((pos = d.find('\n', pos+len))!=string::npos)
-                                       ++pos;
-                       }
-               }
-
-               return pos;
-       }
-
-       complete = true;
-       return 0;
-}
-
-unsigned Message::parse_headers(const string &d)
-{
-       unsigned start = 0;
-       while(1)
-       {
-               unsigned lf = d.find('\n', start);
-               if(lf==string::npos)
-                       throw invalid_argument("Message::parse_headers");
-               if(lf==start || (d[start]=='\r' && lf==start+1))
-                       return lf+1;
-
-               unsigned colon = d.find(':', start);
-               if(colon>lf)
-                       throw invalid_argument("Message::parse_headers");
-
-               set_header(d.substr(start, colon-start), strip(d.substr(colon+1, lf-colon-1)));
-
-               start = lf+1;
-       }
-}
-
-string Message::str_common() const
-{
-       string result;
-
-       for(HeaderMap::const_iterator i=headers.begin(); i!=headers.end(); ++i)
-               if(i->first[0]!='-')
-                       result += format("%s: %s\r\n", i->first, i->second);
-       result += "\r\n";
-       result += content;
-
-       return result;
-}
-
-string Message::normalize_header_name(const string &hdr) const
-{
-       string result = hdr;
-       bool upper = true;
-       for(string::iterator i=result.begin(); i!=result.end(); ++i)
-       {
-               if(upper)
-               {
-                       *i = toupper(*i);
-                       upper = false;
-               }
-               else if(*i=='-')
-                       upper = true;
-               else
-                       *i = tolower(*i);
-       }
-       return result;
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/message.h b/source/message.h
deleted file mode 100644 (file)
index 4d23102..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef MSP_HTTP_MESSAGE_H_
-#define MSP_HTTP_MESSAGE_H_
-
-#include <map>
-#include <string>
-#include <msp/core/variant.h>
-#include "version.h"
-
-namespace Msp {
-namespace Http {
-
-class Message
-{
-protected:
-       typedef std::map<std::string, std::string> HeaderMap;
-
-       Version http_version;
-       HeaderMap headers;
-       std::string content;
-       unsigned chunk_length;
-       bool complete;
-       Variant user_data;
-
-       Message();
-public:
-       virtual ~Message() { }
-
-       void set_header(const std::string &, const std::string &);
-       bool has_header(const std::string &) const;
-       const std::string &get_header(const std::string &) const;
-       void add_content(const std::string &);
-       const std::string &get_content() const { return content; }
-       void set_user_data(const Variant &);
-       const Variant &get_user_data() const { return user_data; }
-       bool is_complete() const { return complete; }
-       unsigned parse_content(const std::string &);
-       virtual std::string str() const = 0;
-protected:
-       unsigned parse_headers(const std::string &);
-       std::string str_common() const;
-       std::string normalize_header_name(const std::string &) const;
-};
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/request.cpp b/source/request.cpp
deleted file mode 100644 (file)
index c948755..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <msp/strings/formatter.h>
-#include <msp/strings/regex.h>
-#include <msp/strings/utils.h>
-#include "request.h"
-#include "utils.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-Request::Request(const string &m, const string &p):
-       method(m),
-       path(p)
-{ }
-
-string Request::str() const
-{
-       string result = format("%s %s %s\r\n", method, path, version_str(http_version));
-       result += str_common();
-
-       return result;
-}
-
-Request Request::parse(const string &str)
-{
-       unsigned lf = str.find('\n');
-       vector<string> parts = split(str.substr(0, lf-(str[lf-1]=='\r')), ' ', 2);
-       if(parts.size()<3)
-               throw invalid_argument("Request::parse");
-
-       Request result(parts[0], parts[1]);
-       result.http_version = parse_version(parts[2]);
-
-       lf += result.parse_headers(str.substr(lf+1));
-
-       result.parse_content(str.substr(lf+1));
-
-       return result;
-}
-
-Request Request::from_url(const string &str)
-{
-       Url url = parse_url(str);
-       if(url.scheme!="http")
-               throw invalid_argument("Request::from_url");
-
-       string path = url.path;
-       if(path.empty())
-               path = "/";
-       if(!url.query.empty())
-       {
-               path += '?';
-               path += url.query;
-       }
-
-       Request result("GET", path);
-       result.set_header("Host", url.host);
-       result.set_header("Connection", "close");
-
-       return result;
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/request.h b/source/request.h
deleted file mode 100644 (file)
index ec5030e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MSP_HTTP_REQUEST_H_
-#define MSP_HTTP_REQUEST_H_
-
-#include <string>
-#include "message.h"
-
-namespace Msp {
-namespace Http {
-
-class Request: public Message
-{
-private:
-       std::string method;
-       std::string path;
-
-public:
-       Request(const std::string &, const std::string &);
-       const std::string &get_method() const { return method; }
-       const std::string &get_path() const { return path; }
-       virtual std::string str() const;
-
-       static Request parse(const std::string &);
-       static Request from_url(const std::string &);
-};
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/response.cpp b/source/response.cpp
deleted file mode 100644 (file)
index ede249b..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <msp/strings/formatter.h>
-#include <msp/strings/utils.h>
-#include "response.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-Response::Response(Status s):
-       status(s)
-{ }
-
-string Response::str() const
-{
-       string result = format("%s %d %s\r\n", version_str(http_version), static_cast<int>(status), status);
-       result += str_common();
-
-       return result;
-}
-
-Response Response::parse(const string &str)
-{
-       Response result;
-
-       unsigned lf = str.find('\n');
-       vector<string> parts = split(str.substr(0, lf), ' ', 2);
-       if(parts.size()<2)
-               throw invalid_argument("Response::parse");
-
-       result.http_version = parse_version(parts[0]);
-       result.status = static_cast<Status>(lexical_cast<unsigned>(parts[1]));
-
-       lf += result.parse_headers(str.substr(lf+1));
-
-       result.parse_content(str.substr(lf+1));
-
-       return result;
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/response.h b/source/response.h
deleted file mode 100644 (file)
index d721ad8..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MSP_HTTP_RESPONSE_H_
-#define MSP_HTTO_RESPONSE_H_
-
-#include "message.h"
-#include "status.h"
-
-namespace Msp {
-namespace Http {
-
-class Response: public Message
-{
-private:
-       Status status;
-
-       Response() { }
-public:
-       Response(Status);
-       Status get_status() const { return status; }
-       virtual std::string str() const;
-
-       static Response parse(const std::string &);
-};
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/server.cpp b/source/server.cpp
deleted file mode 100644 (file)
index 9205cc1..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-#include <exception>
-#include <msp/core/refptr.h>
-#include <msp/net/inet.h>
-#include <msp/net/resolve.h>
-#include <msp/net/streamsocket.h>
-#include <msp/strings/format.h>
-#include "request.h"
-#include "response.h"
-#include "server.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-Server::Server(unsigned port):
-       sock(Net::INET),
-       event_disp(0)
-{
-       sock.signal_data_available.connect(sigc::mem_fun(this, &Server::data_available));
-       RefPtr<Net::SockAddr> addr = Net::resolve("*", format("%d", port));
-       sock.listen(*addr, 8);
-}
-
-unsigned Server::get_port() const
-{
-       const Net::SockAddr &addr = sock.get_local_address();
-       if(addr.get_family()==Net::INET)
-               return static_cast<const Net::InetAddr &>(addr).get_port();
-       return 0;
-}
-
-void Server::use_event_dispatcher(IO::EventDispatcher *ed)
-{
-       if(event_disp)
-       {
-               event_disp->remove(sock);
-               for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
-                       event_disp->remove(*i->sock);
-       }
-       event_disp = ed;
-       if(event_disp)
-       {
-               event_disp->add(sock);
-               for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
-                       event_disp->add(*i->sock);
-       }
-}
-
-void Server::delay_response(Response &resp)
-{
-       get_client_by_response(resp).async = true;
-}
-
-void Server::submit_response(Response &resp)
-{
-       Client &cl = get_client_by_response(resp);
-       if(cl.async)
-       {
-               cl.sock->write(resp.str());
-               cl.stale = true;
-       }
-}
-
-void Server::data_available()
-{
-       Net::StreamSocket *csock = sock.accept();
-       clients.push_back(Client(csock));
-       csock->signal_data_available.connect(sigc::bind(sigc::mem_fun(this, &Server::client_data_available), sigc::ref(clients.back())));
-       csock->signal_end_of_file.connect(sigc::bind(sigc::mem_fun(this, &Server::client_end_of_file), sigc::ref(clients.back())));
-       if(event_disp)
-               event_disp->add(*csock);
-}
-
-void Server::client_data_available(Client &cl)
-{
-       for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
-               if(i->stale && &*i!=&cl)
-               {
-                       clients.erase(i);
-                       break;
-               }
-
-       char rbuf[4096];
-       unsigned len = cl.sock->read(rbuf, sizeof(rbuf));
-       cl.in_buf.append(rbuf, len);
-
-       RefPtr<Response> response;
-       if(!cl.request)
-       {
-               if(cl.in_buf.find("\r\n\r\n")!=string::npos || cl.in_buf.find("\n\n")!=string::npos)
-               {
-                       try
-                       {
-                               cl.request = new Request(Request::parse(cl.in_buf));
-
-                               string addr_str = cl.sock->get_peer_address().str();
-                               unsigned 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")
-                               {
-                                       response = new Response(NOT_IMPLEMENTED);
-                                       response->add_content("Method not implemented\n");
-                               }
-                       }
-                       catch(const exception &e)
-                       {
-                               response = new Response(BAD_REQUEST);
-                               response->add_content(e.what());
-                       }
-                       cl.in_buf = string();
-               }
-       }
-       else
-       {
-               len = cl.request->parse_content(cl.in_buf);
-               cl.in_buf.erase(0, len);
-       }
-
-       if(cl.request && cl.request->is_complete() && !response)
-       {
-               response = new Response(NONE);
-               try
-               {
-                       cl.response = response.get();
-                       signal_request.emit(*cl.request, *response);
-                       if(cl.async)
-                               response.release();
-                       else
-                       {
-                               cl.response = 0;
-                               if(response->get_status()==NONE)
-                               {
-                                       response = new Response(NOT_FOUND);
-                                       response->add_content("The requested resource was not found\n");
-                               }
-                       }
-               }
-               catch(const exception &e)
-               {
-                       cl.response = 0;
-                       response = new Response(INTERNAL_ERROR);
-                       response->add_content(e.what());
-               }
-       }
-
-       if(response)
-       {
-               cl.sock->write(response->str());
-               cl.stale = true;
-       }
-}
-
-void Server::client_end_of_file(Client &cl)
-{
-       cl.stale = true;
-}
-
-Server::Client &Server::get_client_by_response(Response &resp)
-{
-       for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
-               if(i->response==&resp)
-                       return *i;
-
-       // XXX Do this differently
-       throw invalid_argument("Response does not belong to any client");
-}
-
-
-Server::Client::Client(RefPtr<Net::StreamSocket> s):
-       sock(s),
-       request(0),
-       response(0),
-       async(false),
-       stale(false)
-{ }
-
-Server::Client::~Client()
-{
-       delete request;
-       delete response;
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/server.h b/source/server.h
deleted file mode 100644 (file)
index 41894db..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef MSP_HTTP_SERVER_H_
-#define MSP_HTTP_SERVER_H_
-
-#include <msp/core/refptr.h>
-#include <msp/io/eventdispatcher.h>
-#include <msp/net/streamserversocket.h>
-
-namespace Msp {
-namespace Http {
-
-class Request;
-class Response;
-
-class Server
-{
-public:
-       sigc::signal<void, const Request &, Response &> signal_request;
-
-private:
-       struct Client
-       {
-               RefPtr<Net::StreamSocket> sock;
-               std::string in_buf;
-               Request *request;
-               Response *response;
-               bool async;
-               bool stale;
-
-               Client(RefPtr<Net::StreamSocket>);
-               ~Client();
-       };
-
-       Net::StreamServerSocket sock;
-       std::list<Client> clients;
-       IO::EventDispatcher *event_disp;
-
-public:
-       Server(unsigned);
-       unsigned get_port() const;
-       void use_event_dispatcher(IO::EventDispatcher *);
-       void delay_response(Response &);
-       void submit_response(Response &);
-private:
-       void data_available();
-       void client_data_available(Client &);
-       void client_end_of_file(Client &);
-       Client &get_client_by_response(Response &);
-};
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/status.cpp b/source/status.cpp
deleted file mode 100644 (file)
index 6b95822..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "status.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-ostream &operator<<(ostream &out, Status status)
-{
-       switch(status)
-       {
-       case NONE: out<<"None"; break;
-       case OK: out<<"OK"; break;
-       case BAD_REQUEST: out<<"Bad Request"; break;
-       case FORBIDDEN: out<<"Forbidden"; break;
-       case NOT_FOUND: out<<"Not Found"; break;
-       case INTERNAL_ERROR: out<<"Internal Error"; break;
-       case NOT_IMPLEMENTED: out<<"Not Implemented"; break;
-       default: out<<"Unknown Status"; break;
-       }
-
-       return out;
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/status.h b/source/status.h
deleted file mode 100644 (file)
index 075016d..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef MSP_HTTPSERVER_STATUS_H_
-#define MSP_HTTPSERVER_STATUS_H_
-
-#include <ostream>
-
-namespace Msp {
-namespace Http {
-
-enum Status
-{
-       NONE = 0,
-       OK = 200,
-       BAD_REQUEST = 400,
-       FORBIDDEN = 403,
-       NOT_FOUND = 404,
-       INTERNAL_ERROR = 500,
-       NOT_IMPLEMENTED = 501
-};
-
-extern std::ostream &operator<<(std::ostream &, Status);
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/utils.cpp b/source/utils.cpp
deleted file mode 100644 (file)
index 5a5dc04..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-#include <algorithm>
-#include <msp/strings/formatter.h>
-#include <msp/strings/regex.h>
-#include <msp/strings/utils.h>
-#include "utils.h"
-
-using namespace std;
-
-namespace {
-
-const char *reserved[]=
-{
-       " #%&+=?",
-       " #%&*+:;=?@[]",
-       " !#$%&'()*+,/:;=?@[]",
-};
-
-bool is_reserved(char c, unsigned level)
-{
-       for(const char *r=reserved[level]; *r; ++r)
-               if(c==*r)
-                       return true;
-       return false;
-}
-
-}
-
-namespace Msp {
-namespace Http {
-
-string urlencode(const string &str, EncodeLevel level)
-{
-       string result;
-       for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
-       {
-               if(is_reserved(*i, level))
-                       result += format("%%%02X", *i);
-               else
-                       result += *i;
-       }
-       return result;
-}
-
-string urlencode_plus(const string &str, EncodeLevel level)
-{
-       string result;
-       for(string::const_iterator i=str.begin(); i!=str.end(); ++i)
-       {
-               if(*i==' ')
-                       result += '+';
-               else if(is_reserved(*i, level))
-                       result += format("%%%02X", *i);
-               else
-                       result += *i;
-       }
-       return result;
-}
-
-string urldecode(const string &str)
-{
-       string result;
-       for(unsigned i=0; i<str.size(); ++i)
-       {
-               char c = str[i];
-               if(c=='%')
-               {
-                       if(i+3>str.size())
-                               throw invalid_argument("urldecode");
-                       result += lexical_cast<unsigned char>(str.substr(i+1, 2), "x");
-                       i += 2;
-               }
-               else if(c=='+')
-                       result += ' ';
-               else
-                       result += c;
-       }
-       return result;
-}
-
-Url parse_url(const string &str)
-{
-       static Regex r_url("(([a-z]+)://)?([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*(:[0-9])?)?(/[^?#]*)?(\\?([^#]+))?(#(.*))?");
-       if(RegMatch m = r_url.match(str))
-       {
-               Url url;
-               url.scheme = m[2].str;
-               url.host = m[3].str;
-               url.path = urldecode(m[6].str);
-               url.query = m[8].str;
-               url.fragment = m[10].str;
-               return url;
-       }
-       else
-               throw invalid_argument("parse_url");
-}
-
-string build_url(const Url &url)
-{
-       if(!url.path.empty() && url.path[0]!='/')
-               throw invalid_argument("build_url");
-
-       string str;
-       if(!url.scheme.empty())
-               str += url.scheme+"://";
-       str += url.host;
-       str += urlencode(url.path);
-       if(!url.query.empty())
-       {
-               str += '?';
-               str += url.query;
-       }
-       if(!url.fragment.empty())
-       {
-               str += '#';
-               str += url.fragment;
-       }
-       return str;
-}
-
-Query parse_query(const std::string &str)
-{
-       vector<string> parts = split(str, '&');
-       Query query;
-       for(vector<string>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
-       {
-               unsigned equals = i->find('=');
-               string &value = query[urldecode(i->substr(0, equals))];
-               if(equals!=string::npos)
-                       value = urldecode(i->substr(equals+1));
-       }
-       return query;
-}
-
-string build_query(const Query &query)
-{
-       string str;
-       for(Query::const_iterator i=query.begin(); i!=query.end(); ++i)
-       {
-               if(i!=query.begin())
-                       str += '&';
-               str += urlencode_plus(i->first);
-               str += '=';
-               str += urlencode_plus(i->second);
-       }
-       return str;
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/utils.h b/source/utils.h
deleted file mode 100644 (file)
index a25a748..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef MSP_HTTP_UTILS_H_
-#define MSP_HTTP_UTILS_H_
-
-#include <map>
-#include <string>
-
-namespace Msp {
-namespace Http {
-
-enum EncodeLevel
-{
-       MINIMAL,
-       SAFE,
-       PARANOID
-};
-
-struct Url
-{
-       std::string scheme;
-       std::string host;
-       std::string path;
-       std::string query;
-       std::string fragment;
-};
-
-typedef std::map<std::string, std::string> Query;
-
-std::string urlencode(const std::string &, EncodeLevel =SAFE);
-std::string urlencode_plus(const std::string &, EncodeLevel =SAFE);
-std::string urldecode(const std::string &);
-Url parse_url(const std::string &);
-std::string build_url(const Url &);
-Query parse_query(const std::string &);
-std::string build_query(const Query &);
-
-} // namespace Http
-} // namespace Msp
-
-#endif
diff --git a/source/version.cpp b/source/version.cpp
deleted file mode 100644 (file)
index f22b46e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <msp/strings/formatter.h>
-#include <msp/strings/lexicalcast.h>
-#include <msp/strings/regex.h>
-#include "version.h"
-
-using namespace std;
-
-namespace Msp {
-namespace Http {
-
-Version parse_version(const std::string &ver)
-{
-       if(RegMatch match = Regex("^HTTP/([0-9]+).([0-9]+)$").match(ver))
-               return lexical_cast<unsigned>(match[1].str)<<4 | lexical_cast<unsigned>(match[2].str);
-       else
-               throw invalid_argument("parse_version");
-}
-
-string version_str(Version ver)
-{
-       return format("HTTP/%u.%u", (ver>>4)&0xF, ver&0xF);
-}
-
-} // namespace Http
-} // namespace Msp
diff --git a/source/version.h b/source/version.h
deleted file mode 100644 (file)
index 19a50b9..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef MSP_HTTP_MISC_H_
-#define MSP_HTTP_MISC_H_
-
-#include <string>
-
-namespace Msp {
-namespace Http {
-
-typedef unsigned Version;
-
-Version parse_version(const std::string &);
-std::string version_str(Version);
-
-} // namespace Http
-} // namespace Msp
-
-#endif