From 4c4ae6ac093a7b9de1bf8e0204abeb88a14e9edf Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 11 Jul 2014 12:43:58 +0200 Subject: [PATCH] Added default GET-example --- main.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++++-- server.hpp | 41 ++++++++++++++++++++++++++----------- web/index.html | 8 ++++++++ web/test.html | 8 ++++++++ 4 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 web/index.html create mode 100644 web/test.html diff --git a/main.cpp b/main.cpp index 969c605..68382dc 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,8 @@ #include #include +#include + using namespace std; using namespace SimpleWeb; //Added for the json-example: @@ -46,9 +48,9 @@ int main() { } }; - //GET-example for the path / + //GET-example for the path /info //Responds with request-information - httpserver.resources["^/$"]["GET"]=[](ostream& response, const Request& request, const smatch& path_match) { + httpserver.resources["^/info/?$"]["GET"]=[](ostream& response, const Request& request, const smatch& path_match) { stringstream content_stream; content_stream << "

Request:

"; content_stream << request.method << " " << request.path << " HTTP/" << request.http_version << "
"; @@ -69,6 +71,55 @@ int main() { response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number; }; + //Default GET-example. If no other matches, this anonymous function will be called. + //Will respond with content in the web/-directory, and its subdirectories. + //Default file: index.html + //Can be used to retrieve an HTML 5 client using REST-resources on this server + httpserver.default_resource["^/?(.*)$"]["GET"]=[](ostream& response, const Request& request, const smatch& path_match) { + string filename="web/"; + + string path=path_match[1]; + + //Remove all but the last '.' (so we can't leave the web-directory) + size_t last_pos=path.rfind("."); + size_t current_pos=0; + size_t pos; + while((pos=path.find('.', current_pos))!=string::npos && pos!=last_pos) { + cout << pos << endl; + current_pos=pos; + path.erase(pos, 1); + last_pos--; + } + + filename+=path; + ifstream ifs; + //HTTP file-or-directory check: + if(filename.find('.')!=string::npos) { + ifs.open(filename, ifstream::in); + } + else { + if(filename[filename.length()-1]!='/') + filename+='/'; + filename+="index.html"; + ifs.open(filename, ifstream::in); + } + + if(ifs) { + ifs.seekg(0, ios::end); + size_t length=ifs.tellg(); + + ifs.seekg(0, ios::beg); + + response << "HTTP/1.1 200 OK\r\nContent-Length: " << length << "\r\n\r\n" << ifs.rdbuf(); + + ifs.close(); + } + else { + string content="Could not open file "+filename; + response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; + } + }; + //Start HTTP-server httpserver.start(); diff --git a/server.hpp b/server.hpp index f36a2b8..7db6e94 100644 --- a/server.hpp +++ b/server.hpp @@ -21,17 +21,30 @@ namespace SimpleWeb { unordered_map header; }; - template + typedef map > > resource_type; + + template class Server { public: - unordered_map > > resources; + resource_type resources; + resource_type default_resource; + Server(unsigned short port, size_t num_threads=1) : endpoint(ip::tcp::v4(), port), acceptor(m_io_service, endpoint), num_threads(num_threads) {} - + void start() { - accept(); - + //All resources with default_resource at the end of vector + //Used in the respond-method + for(auto it=resources.begin(); it!=resources.end();it++) { + all_resources.push_back(it); + } + for(auto it=default_resource.begin(); it!=default_resource.end();it++) { + all_resources.push_back(it); + } + + accept(); + //If num_threads>1, start m_io_service.run() in (num_threads-1) threads for thread-pooling for(size_t c=1;c threads; + //All resources with default_resource at the end of vector + //Created in start() + vector all_resources; + void accept() { //Create new socket for this connection //Shared_ptr is used to pass temporary objects to the asynchronous functions - shared_ptr socket(new type(m_io_service)); + shared_ptr socket(new socket_type(m_io_service)); acceptor.async_accept(*socket, [this, socket](const boost::system::error_code& ec) { //Immediately start accepting a new connection @@ -70,7 +87,7 @@ namespace SimpleWeb { }); } - void process_request_and_respond(shared_ptr socket) { + void process_request_and_respond(shared_ptr socket) { //Create new read_buffer for async_read_until() //Shared_ptr is used to pass temporary objects to the asynchronous functions shared_ptr read_buffer(new boost::asio::streambuf); @@ -144,16 +161,16 @@ namespace SimpleWeb { return request; } - void respond(shared_ptr socket, shared_ptr request) { + void respond(shared_ptr socket, shared_ptr request) { //Find path- and method-match, and generate response - for(auto& res: resources) { - regex e(res.first); + for(auto res_it: all_resources) { + regex e(res_it->first); smatch sm_res; if(regex_match(request->path, sm_res, e)) { - if(res.second.count(request->method)>0) { + if(res_it->second.count(request->method)>0) { shared_ptr write_buffer(new boost::asio::streambuf); ostream response(write_buffer.get()); - res.second[request->method](response, *request, sm_res); + res_it->second[request->method](response, *request, sm_res); //Capture write_buffer in lambda so it is not destroyed before async_write is finished async_write(*socket, *write_buffer, [this, socket, request, write_buffer](const boost::system::error_code& ec, size_t bytes_transferred) { diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..3cf66e4 --- /dev/null +++ b/web/index.html @@ -0,0 +1,8 @@ + + + Simple-Web-Server html-file + + + This is the content of index.html + + diff --git a/web/test.html b/web/test.html new file mode 100644 index 0000000..af5fe1c --- /dev/null +++ b/web/test.html @@ -0,0 +1,8 @@ + + + Simple-Web-Server html-file + + + This is the content of test.html + +