diff --git a/CMakeLists.txt b/CMakeLists.txt index f5d7cff..4b54ca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ else() endif() #Only tested with versions 1.55 and 1.56 -find_package(Boost 1.54.0 COMPONENTS system thread coroutine context REQUIRED) +find_package(Boost 1.54.0 COMPONENTS system thread coroutine context filesystem REQUIRED) message("Boost include dir: ${Boost_INCLUDE_DIR}") message("Boost libraries: ${Boost_LIBRARIES}") include_directories(${Boost_INCLUDE_DIR}) diff --git a/README.md b/README.md index fd11b55..36539d8 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Compile with a C++11 compiler supporting regex (for instance g++ 4.9): On Linux using g++: add `-pthread` -Note: added `-lboost_thread` to make the json-example thread safe. Also added `-lboost_coroutine -lboost_context` to make synchronous and asynchronous flushing of response stream work. On some systems you might have to use postfix `-mt` to link to these libraries. +Note: added `-lboost_filesystem` for the default_resource example, and `-lboost_thread` to make the json-example thread safe. Also added `-lboost_coroutine -lboost_context` to make synchronous and asynchronous flushing of response stream work. On some systems you might have to use postfix `-mt` to link to these libraries. You can now also compile using CMake and make: @@ -49,7 +49,7 @@ make #### HTTP -`g++ -O3 -std=c++11 http_examples.cpp -lboost_system -lboost_thread -lboost_coroutine -lboost_context -o http_examples` +`g++ -O3 -std=c++11 http_examples.cpp -lboost_system -lboost_thread -lboost_coroutine -lboost_context -lboost_filesystem -o http_examples` Then to run the server and client examples: `./http_examples` @@ -57,7 +57,7 @@ Also, direct your favorite browser to for instance http://localhost:8080/ #### HTTPS -`g++ -O3 -std=c++11 https_examples.cpp -lboost_system -lboost_thread -lboost_coroutine -lboost_context -lssl -lcrypto -o https_examples` +`g++ -O3 -std=c++11 https_examples.cpp -lboost_system -lboost_thread -lboost_coroutine -lboost_context -lboost_filesystem -lssl -lcrypto -o https_examples` Before running the server, an RSA private key (server.key) and an SSL certificate (server.crt) must be created. Follow, for instance, the instructions given here (for a self-signed certificate): http://www.akadia.com/services/ssh_test_certificate.html diff --git a/http_examples.cpp b/http_examples.cpp index 56746b1..de92d40 100644 --- a/http_examples.cpp +++ b/http_examples.cpp @@ -7,7 +7,8 @@ #include //Added for the default_resource example -#include +#include +#include using namespace std; //Added for the json-example: @@ -81,53 +82,50 @@ int main() { //Default file: index.html //Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server server.default_resource["GET"]=[](HttpServer::Response& response, shared_ptr request) { - string filename="web"; - - string path=request->path; - - //Replace all ".." with "." (so we can't leave the web-directory) - size_t pos; - while((pos=path.find(".."))!=string::npos) { - path.erase(pos, 1); - } - - filename+=path; - ifstream ifs; - //A simple platform-independent file-or-directory check do not exist, but this works in most of the cases: - if(filename.find('.')==string::npos) { - 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"; - - //read and send 128 KB at a time if file-size>buffer_size - size_t buffer_size=131072; - if(length>buffer_size) { - vector buffer(buffer_size); - size_t read_length; - while((read_length=ifs.read(&buffer[0], buffer_size).gcount())>0) { - response.stream.write(&buffer[0], read_length); - response << HttpServer::flush; - } - } - else - response << ifs.rdbuf(); - - ifs.close(); - } + boost::filesystem::path web_root_path("web"); + if(!boost::filesystem::exists(web_root_path)) + cerr << "Could not find web root." << endl; 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; + auto path=web_root_path; + path+=request->path; + if(boost::filesystem::exists(path)) { + if(boost::filesystem::canonical(web_root_path)<=boost::filesystem::canonical(path)) { + if(boost::filesystem::is_directory(path)) + path+="/index.html"; + if(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)) { + ifstream ifs; + ifs.open(path.string(), 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"; + + //read and send 128 KB at a time if file-size>buffer_size + size_t buffer_size=131072; + if(length>buffer_size) { + vector buffer(buffer_size); + size_t read_length; + while((read_length=ifs.read(&buffer[0], buffer_size).gcount())>0) { + response.stream.write(&buffer[0], read_length); + response << HttpServer::flush; + } + } + else + response << ifs.rdbuf(); + + ifs.close(); + return; + } + } + } + } } + string content="Could not open path "+request->path; + response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; }; thread server_thread([&server](){ diff --git a/https_examples.cpp b/https_examples.cpp index 8b010d7..bc3082a 100644 --- a/https_examples.cpp +++ b/https_examples.cpp @@ -7,7 +7,8 @@ #include //Added for the default_resource example -#include +#include +#include using namespace std; //Added for the json-example: @@ -81,53 +82,50 @@ int main() { //Default file: index.html //Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server server.default_resource["GET"]=[](HttpsServer::Response& response, shared_ptr request) { - string filename="web"; - - string path=request->path; - - //Replace all ".." with "." (so we can't leave the web-directory) - size_t pos; - while((pos=path.find(".."))!=string::npos) { - path.erase(pos, 1); - } - - filename+=path; - ifstream ifs; - //A simple platform-independent file-or-directory check do not exist, but this works in most of the cases: - if(filename.find('.')==string::npos) { - 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"; - - //read and send 128 KB at a time if file-size>buffer_size - size_t buffer_size=131072; - if(length>buffer_size) { - vector buffer(buffer_size); - size_t read_length; - while((read_length=ifs.read(&buffer[0], buffer_size).gcount())>0) { - response.stream.write(&buffer[0], read_length); - response << HttpsServer::flush; - } - } - else - response << ifs.rdbuf(); - - ifs.close(); - } + boost::filesystem::path web_root_path("web"); + if(!boost::filesystem::exists(web_root_path)) + cerr << "Could not find web root." << endl; 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; + auto path=web_root_path; + path+=request->path; + if(boost::filesystem::exists(path)) { + if(boost::filesystem::canonical(web_root_path)<=boost::filesystem::canonical(path)) { + if(boost::filesystem::is_directory(path)) + path+="/index.html"; + if(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)) { + ifstream ifs; + ifs.open(path.string(), 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"; + + //read and send 128 KB at a time if file-size>buffer_size + size_t buffer_size=131072; + if(length>buffer_size) { + vector buffer(buffer_size); + size_t read_length; + while((read_length=ifs.read(&buffer[0], buffer_size).gcount())>0) { + response.stream.write(&buffer[0], read_length); + response << HttpsServer::flush; + } + } + else + response << ifs.rdbuf(); + + ifs.close(); + return; + } + } + } + } } + string content="Could not open path "+request->path; + response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; }; thread server_thread([&server](){