├── http_web_server ├── test │ ├── php │ │ ├── phptest_1.php │ │ └── phptest_2.php │ ├── perl │ │ ├── hello.pl │ │ ├── print_env.cgi │ │ ├── form.pl │ │ └── perl_test_2.html │ ├── html │ │ ├── demo_1.html │ │ ├── demo_2.html │ │ └── form_validation.html │ ├── python │ │ ├── hello.py │ │ ├── py_demo_2.html │ │ ├── checkbox.html │ │ ├── hello_get.py │ │ └── checkbox.cgi │ └── test.html ├── images │ ├── http_server_test.png │ └── http_server_main_page.png ├── HttpAcceptor.hpp ├── HttpServer.hpp ├── Makefile ├── main.cpp ├── HttpServer.cpp ├── HttpAcceptor.cpp ├── README.md ├── HttpService.hpp └── HttpService.cpp ├── https_web_server ├── test │ ├── php │ │ ├── phptest_1.php │ │ └── phptest_2.php │ ├── perl │ │ ├── hello.pl │ │ ├── print_env.cgi │ │ ├── form.pl │ │ └── perl_test_2.html │ ├── html │ │ ├── demo_1.html │ │ ├── demo_2.html │ │ └── form_validation.html │ ├── python │ │ ├── hello.py │ │ ├── py_demo_2.html │ │ ├── checkbox.html │ │ ├── hello_get.py │ │ └── checkbox.cgi │ └── test.html ├── images │ ├── allow_https_request.png │ └── https_web_server_main_page.png ├── gen_openssl_key.sh ├── HttpsServer.hpp ├── Makefile ├── HttpsAcceptor.hpp ├── HttpsServer.cpp ├── main.cpp ├── HttpsAcceptor.cpp ├── README.md ├── HttpsService.hpp └── HttpsService.cpp └── README.md /http_web_server/test/php/phptest_1.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /https_web_server/test/php/phptest_1.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /http_web_server/test/perl/hello.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | 4 | print 'Hello world'; 5 | -------------------------------------------------------------------------------- /https_web_server/test/perl/hello.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | 4 | print 'Hello world'; 5 | -------------------------------------------------------------------------------- /http_web_server/images/http_server_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pritamzope/http_web_server/HEAD/http_web_server/images/http_server_test.png -------------------------------------------------------------------------------- /http_web_server/images/http_server_main_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pritamzope/http_web_server/HEAD/http_web_server/images/http_server_main_page.png -------------------------------------------------------------------------------- /https_web_server/images/allow_https_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pritamzope/http_web_server/HEAD/https_web_server/images/allow_https_request.png -------------------------------------------------------------------------------- /https_web_server/images/https_web_server_main_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pritamzope/http_web_server/HEAD/https_web_server/images/https_web_server_main_page.png -------------------------------------------------------------------------------- /https_web_server/gen_openssl_key.sh: -------------------------------------------------------------------------------- 1 | # generate SSL certificate and private key 2 | openssl req -newkey rsa:2048 -nodes -keyout HttpsWebServer.key -x509 -days 365 -out HttpsWebServer.cert 3 | -------------------------------------------------------------------------------- /http_web_server/test/html/demo_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

HTML

7 |

HTML

8 |

HTML

9 |

HTML

10 |
HTML
11 |
HTML
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /https_web_server/test/html/demo_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

HTML

7 |

HTML

8 |

HTML

9 |

HTML

10 |
HTML
11 |
HTML
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /http_web_server/test/perl/print_env.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | =head1 DESCRIPTION 4 | 5 | printenv — a CGI program that just prints its environment 6 | 7 | =cut 8 | 9 | for my $var ( sort keys %ENV ) { 10 | printf "%s = \"%s\"\n", $var, $ENV{$var}; 11 | } 12 | -------------------------------------------------------------------------------- /https_web_server/test/perl/print_env.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | =head1 DESCRIPTION 4 | 5 | printenv — a CGI program that just prints its environment 6 | 7 | =cut 8 | 9 | for my $var ( sort keys %ENV ) { 10 | printf "%s = \"%s\"\n", $var, $ENV{$var}; 11 | } 12 | -------------------------------------------------------------------------------- /http_web_server/test/python/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | print '' 4 | print '' 5 | print 'Hello Word' 6 | print '' 7 | print '' 8 | print '

Hello Word! This is my first CGI program

' 9 | print '' 10 | print '' 11 | 12 | -------------------------------------------------------------------------------- /https_web_server/test/python/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | print '' 4 | print '' 5 | print 'Hello Word' 6 | print '' 7 | print '' 8 | print '

Hello Word! This is my first CGI program

' 9 | print '' 10 | print '' 11 | 12 | -------------------------------------------------------------------------------- /http_web_server/test/php/phptest_2.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | "; 12 | } 13 | ?> 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /https_web_server/test/php/phptest_2.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | "; 12 | } 13 | ?> 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /http_web_server/test/python/py_demo_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | First Name:
6 | Last Name: 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /https_web_server/test/python/py_demo_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | First Name:
6 | Last Name: 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /http_web_server/test/python/checkbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | Maths 8 | Physics 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /https_web_server/test/python/checkbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | Maths 8 | Physics 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /http_web_server/test/python/hello_get.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Import modules for CGI handling 4 | import cgi, cgitb 5 | 6 | # Create instance of FieldStorage 7 | form = cgi.FieldStorage() 8 | 9 | # Get data from fields 10 | first_name = form.getvalue('first_name') 11 | last_name = form.getvalue('last_name') 12 | 13 | print "" 14 | print "" 15 | print "Hello - Second CGI Program" 16 | print "" 17 | print "" 18 | print "

Hello %s %s

" % (first_name, last_name) 19 | print "" 20 | print "" 21 | 22 | 23 | -------------------------------------------------------------------------------- /https_web_server/test/python/hello_get.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Import modules for CGI handling 4 | import cgi, cgitb 5 | 6 | # Create instance of FieldStorage 7 | form = cgi.FieldStorage() 8 | 9 | # Get data from fields 10 | first_name = form.getvalue('first_name') 11 | last_name = form.getvalue('last_name') 12 | 13 | print "" 14 | print "" 15 | print "Hello - Second CGI Program" 16 | print "" 17 | print "" 18 | print "

Hello %s %s

" % (first_name, last_name) 19 | print "" 20 | print "" 21 | 22 | 23 | -------------------------------------------------------------------------------- /https_web_server/HttpsServer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTPSERVER_HPP 2 | #define HTTPSERVER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "HttpsAcceptor.hpp" 11 | 12 | class HttpsServer 13 | { 14 | public: 15 | HttpsServer(); 16 | void Start(unsigned short); 17 | void Stop(); 18 | 19 | private: 20 | 21 | void StartAcceptor(unsigned short); 22 | 23 | std::unique_ptr m_Thread; 24 | std::atomic m_Stop; 25 | boost::asio::io_service m_IOService; 26 | }; 27 | 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /http_web_server/HttpAcceptor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTPACCEPTOR_HPP 2 | #define HTTPACCEPTOR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | //a HTTP coonection acceptor 10 | class HttpAcceptor 11 | { 12 | public: 13 | HttpAcceptor(boost::asio::io_service&, unsigned short); 14 | void Start(); 15 | void Stop(); 16 | 17 | private: 18 | void AcceptConnection(); 19 | 20 | private: 21 | boost::asio::io_service& m_IOService; 22 | boost::asio::ip::tcp::acceptor m_Acceptor; 23 | std::atomic m_IsStopped; 24 | }; 25 | 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /http_web_server/test/perl/form.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use CGI ':standard'; 5 | 6 | my $name = param('name'); 7 | my $age = param('age'); 8 | my $gender = param('gender'); 9 | my @hobbies = param('hobby'); 10 | 11 | my $list; 12 | 13 | if (@hobbies) { 14 | $list = join ', ', @hobbies; 15 | } else { 16 | $list = 'None'; 17 | } 18 | 19 | print header, 20 | start_html(-title=>$name), 21 | h1("Welcome $name"), 22 | p('Here are your details:'), 23 | table(Tr(td('Name:'), 24 | td($name)), 25 | Tr(td('Age:'), 26 | td($age)), 27 | Tr(td('Gender:'), 28 | td($gender)), 29 | Tr(td('Hobbies:'), 30 | td($list))), 31 | end_html; 32 | 33 | -------------------------------------------------------------------------------- /https_web_server/test/perl/form.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use CGI ':standard'; 5 | 6 | my $name = param('name'); 7 | my $age = param('age'); 8 | my $gender = param('gender'); 9 | my @hobbies = param('hobby'); 10 | 11 | my $list; 12 | 13 | if (@hobbies) { 14 | $list = join ', ', @hobbies; 15 | } else { 16 | $list = 'None'; 17 | } 18 | 19 | print header, 20 | start_html(-title=>$name), 21 | h1("Welcome $name"), 22 | p('Here are your details:'), 23 | table(Tr(td('Name:'), 24 | td($name)), 25 | Tr(td('Age:'), 26 | td($age)), 27 | Tr(td('Gender:'), 28 | td($gender)), 29 | Tr(td('Hobbies:'), 30 | td($list))), 31 | end_html; 32 | 33 | -------------------------------------------------------------------------------- /http_web_server/HttpServer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTPSERVER_HPP 2 | #define HTTPSERVER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "HttpAcceptor.hpp" 11 | 12 | class HttpServer 13 | { 14 | public: 15 | HttpServer(); 16 | void Start(unsigned short, unsigned int); 17 | void Stop(); 18 | 19 | private: 20 | boost::asio::io_service m_IOService; 21 | std::unique_ptr m_Work; 22 | std::unique_ptr m_Acceptor; 23 | std::vector> m_ThreadPool; 24 | }; 25 | 26 | 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /http_web_server/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS=-std=c++11 -lboost_system -lboost_filesystem -lpthread 3 | OBJFILES=HttpService.o HttpAcceptor.o HttpServer.o main.o 4 | 5 | httpserver : ${OBJFILES} 6 | ${CXX} ${OBJFILES} -o $@ ${CXXFLAGS} 7 | 8 | HttpService.o : HttpService.cpp 9 | ${CXX} -c HttpService.cpp -o $@ ${CXXFLAGS} 10 | 11 | HttpAcceptor.o : HttpAcceptor.cpp 12 | ${CXX} -c HttpAcceptor.cpp -o $@ ${CXXFLAGS} 13 | 14 | HttpServer.o : HttpServer.cpp 15 | ${CXX} -c HttpServer.cpp -o $@ ${CXXFLAGS} 16 | 17 | main.o : main.cpp 18 | ${CXX} -c main.cpp -o $@ ${CXXFLAGS} 19 | 20 | cleanobj: 21 | rm *.o 22 | 23 | clean: 24 | rm *.o 25 | rm httpserver 26 | 27 | -------------------------------------------------------------------------------- /https_web_server/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS=-std=c++11 -g -lboost_system -lboost_filesystem -lcrypto -lssl -lpthread -I /usr/include/openssl/ 3 | OBJFILES=HttpsService.o HttpsAcceptor.o HttpsServer.o main.o 4 | 5 | httpsserver : ${OBJFILES} 6 | ${CXX} ${OBJFILES} -o $@ ${CXXFLAGS} 7 | 8 | HttpsService.o : HttpsService.cpp 9 | ${CXX} -c HttpsService.cpp -o $@ ${CXXFLAGS} 10 | 11 | HttpsAcceptor.o : HttpsAcceptor.cpp 12 | ${CXX} -c HttpsAcceptor.cpp -o $@ ${CXXFLAGS} 13 | 14 | HttpsServer.o : HttpsServer.cpp 15 | ${CXX} -c HttpsServer.cpp -o $@ ${CXXFLAGS} 16 | 17 | main.o : main.cpp 18 | ${CXX} -c main.cpp -o $@ ${CXXFLAGS} 19 | 20 | cleanobj: 21 | rm *.o 22 | 23 | clean: 24 | rm *.o 25 | rm httpsserver 26 | 27 | -------------------------------------------------------------------------------- /http_web_server/test/python/checkbox.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Import modules for CGI handling 4 | import cgi, cgitb 5 | 6 | # Create instance of FieldStorage 7 | form = cgi.FieldStorage() 8 | 9 | # Get data from fields 10 | if form.getvalue('maths'): 11 | math_flag = "ON" 12 | else: 13 | math_flag = "OFF" 14 | 15 | if form.getvalue('physics'): 16 | physics_flag = "ON" 17 | else: 18 | physics_flag = "OFF" 19 | 20 | print "" 21 | print "" 22 | print "Checkbox - Third CGI Program" 23 | print "" 24 | print "" 25 | print "

CheckBox Maths is : %s

" % math_flag 26 | print "

CheckBox Physics is : %s

" % physics_flag 27 | print "" 28 | print "" 29 | -------------------------------------------------------------------------------- /https_web_server/test/python/checkbox.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Import modules for CGI handling 4 | import cgi, cgitb 5 | 6 | # Create instance of FieldStorage 7 | form = cgi.FieldStorage() 8 | 9 | # Get data from fields 10 | if form.getvalue('maths'): 11 | math_flag = "ON" 12 | else: 13 | math_flag = "OFF" 14 | 15 | if form.getvalue('physics'): 16 | physics_flag = "ON" 17 | else: 18 | physics_flag = "OFF" 19 | 20 | print "" 21 | print "" 22 | print "Checkbox - Third CGI Program" 23 | print "" 24 | print "" 25 | print "

CheckBox Maths is : %s

" % math_flag 26 | print "

CheckBox Physics is : %s

" % physics_flag 27 | print "" 28 | print "" 29 | -------------------------------------------------------------------------------- /https_web_server/HttpsAcceptor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTPACCEPTOR_HPP 2 | #define HTTPACCEPTOR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //a HTTPS coonection acceptor 11 | class HttpsAcceptor 12 | { 13 | public: 14 | HttpsAcceptor(boost::asio::io_service&, unsigned short); 15 | void Start(); 16 | void Stop(); 17 | 18 | private: 19 | void AcceptConnection(); 20 | std::string get_password(std::size_t, boost::asio::ssl::context::password_purpose) const; 21 | 22 | private: 23 | boost::asio::io_service& m_IOService; 24 | boost::asio::ip::tcp::acceptor m_Acceptor; 25 | std::atomic m_IsStopped; 26 | boost::asio::ssl::context m_SSLContext; 27 | }; 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /http_web_server/test/html/demo_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 |
8 |
9 |

CSS = Styles and Colors

10 |
Manipulate Text
11 |
Colors, 12 | Boxes
13 |
14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /https_web_server/test/html/demo_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 |
8 |
9 |

CSS = Styles and Colors

10 |
Manipulate Text
11 |
Colors, 12 | Boxes
13 |
14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # HTTP and HTTPS Web Server 3 | 4 | 5 | # What Is It? 6 | 7 | An asynchronous HTTP web server that supports HTTP web requests as well as PHP, CGI, Python, Perl etc. scripts execution on server sides. 8 | 9 | An synchronous HTTPS web server that supports secure HTTP web requests over Secure Socket Layer(SSL) as well as 10 | PHP, CGI, Python, Perl etc. scripts execution on server sides. 11 | 12 | 13 | # Compilation 14 | 15 | ### Dependency 16 | 17 | ##### GCC with C++11 compiler(g++) 18 | ##### Boost C++ Library 19 | ##### OpenSSL library 20 | 21 | 22 | ## HTTP Web Server Main Page:- 23 | 24 | 25 | 26 | 27 | ## HTTPS Web Server Main Page:- 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /https_web_server/HttpsServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "HttpsServer.hpp" 8 | 9 | using namespace boost; 10 | 11 | HttpsServer::HttpsServer() 12 | { 13 | m_Stop = false; 14 | } 15 | 16 | //start the https server by starting accepting connections 17 | void HttpsServer::Start(unsigned short port) 18 | { 19 | m_Thread.reset(new std::thread([this, port]() { 20 | StartAcceptor(port); 21 | })); 22 | 23 | std::cout<<"Server started at address: 127.0.0.1, port: 1234"<join(); 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /https_web_server/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "HttpsServer.hpp" 6 | 7 | std::string RESOURCE_DIRECTORY_PATH; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | unsigned short port = 1234; 12 | 13 | if(argc < 2){ 14 | std::cout<<"./httpserver "< 2 | #include 3 | #include 4 | 5 | #include "HttpServer.hpp" 6 | 7 | std::string RESOURCE_DIRECTORY_PATH; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | unsigned short port = 1234; 12 | 13 | if(argc < 2){ 14 | std::cout<<"./httpserver "< 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "HttpServer.hpp" 8 | 9 | using namespace boost; 10 | 11 | //create new work of io_service 12 | HttpServer::HttpServer() 13 | { 14 | m_Work.reset(new asio::io_service::work(m_IOService)); 15 | } 16 | 17 | //start the http server 18 | void HttpServer::Start(unsigned short port, unsigned int thread_pool_size) 19 | { 20 | if(thread_pool_size <= 0) return; 21 | 22 | //create and start HttpAcceptor for accepting connection requests 23 | m_Acceptor.reset(new HttpAcceptor(m_IOService, port)); 24 | m_Acceptor->Start(); 25 | 26 | std::cout<<"Server started at address: 127.0.0.1, port: 1234"< 32 | th(new std::thread([this]() 33 | { 34 | //run the socket io service 35 | m_IOService.run(); 36 | }) 37 | ); 38 | 39 | m_ThreadPool.push_back(std::move(th)); 40 | } 41 | } 42 | 43 | //stop the server. 44 | void HttpServer::Stop() 45 | { 46 | m_Acceptor->Stop(); 47 | m_IOService.stop(); 48 | for(auto& th : m_ThreadPool){ 49 | th->join(); 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /http_web_server/test/perl/perl_test_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test Form 4 | 5 | 6 |

Test Form

7 |

Tell us about yourself.

8 |
9 | 10 | 11 | 12 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 44 | 45 |
Name: 13 |
Age:
Gender:Male 28 | Female
Hobbies:Sport 35 | Music 37 | Reading 39 | Beer
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /https_web_server/test/perl/perl_test_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test Form 4 | 5 | 6 |

Test Form

7 |

Tell us about yourself.

8 |
9 | 10 | 11 | 12 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 44 | 45 |
Name: 13 |
Age:
Gender:Male 28 | Female
Hobbies:Sport 35 | Music 37 | Reading 39 | Beer
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /http_web_server/HttpAcceptor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "HttpService.hpp" 8 | #include "HttpAcceptor.hpp" 9 | 10 | using namespace boost; 11 | 12 | //initialize TCP endpoint with IPv4 13 | HttpAcceptor::HttpAcceptor(asio::io_service& ios, unsigned short port_num): 14 | m_IOService(ios), 15 | m_Acceptor(m_IOService, 16 | asio::ip::tcp::endpoint(asio::ip::address_v4::any(), port_num)), 17 | m_IsStopped(false) 18 | {} 19 | 20 | 21 | void HttpAcceptor::Start() 22 | { 23 | m_Acceptor.listen(); 24 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 25 | AcceptConnection(); 26 | } 27 | 28 | void HttpAcceptor::Stop() 29 | { 30 | m_IsStopped.store(true); 31 | } 32 | 33 | //accept the client connection request 34 | void HttpAcceptor::AcceptConnection() 35 | { 36 | std::shared_ptr sock(new asio::ip::tcp::socket(m_IOService)); 37 | 38 | m_Acceptor.async_accept(*sock.get(), 39 | [this, sock](const boost::system::error_code& ec) 40 | { 41 | if(ec == 0){ 42 | (new HttpService(sock))->HttpHandleRequest(); 43 | }else{ 44 | std::cout<<"Error occured, Error code = "< 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "HttpsService.hpp" 9 | #include "HttpsAcceptor.hpp" 10 | 11 | using namespace boost; 12 | 13 | //initialize TCP endpoint with IPv4 and ssl context with sslv23 server 14 | HttpsAcceptor::HttpsAcceptor(boost::asio::io_service& ios, unsigned short port): 15 | m_IOService(ios), 16 | m_Acceptor(m_IOService, 17 | asio::ip::tcp::endpoint(asio::ip::address_v4::any(), port)), 18 | m_IsStopped(false), 19 | m_SSLContext(asio::ssl::context::sslv23_server) 20 | { 21 | //setting up the ssl context. 22 | m_SSLContext.set_options( 23 | boost::asio::ssl::context::default_workarounds | 24 | boost::asio::ssl::context::no_sslv2 | 25 | boost::asio::ssl::context::single_dh_use); 26 | 27 | // set ssl certification file 28 | m_SSLContext.use_certificate_chain_file("HttpsWebServer.cert"); 29 | // set ssl private key file 30 | m_SSLContext.use_private_key_file("HttpsWebServer.key", boost::asio::ssl::context::pem); 31 | } 32 | 33 | 34 | void HttpsAcceptor::Start() 35 | { 36 | // listen for clients 37 | m_Acceptor.listen(); 38 | // create new ssl stream with ioservice & above created context 39 | boost::asio::ssl::stream ssl_stream(m_IOService, m_SSLContext); 40 | 41 | // accept client request 42 | m_Acceptor.accept(ssl_stream.lowest_layer()); 43 | 44 | // create Https service and handle that request 45 | HttpsService service; 46 | service.HttpsHandleRequest(ssl_stream); 47 | 48 | } 49 | 50 | void HttpsAcceptor::Stop() 51 | { 52 | m_IsStopped.store(true); 53 | } 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /http_web_server/test/test.html: -------------------------------------------------------------------------------- 1 | 2 | HTTP Web Server Test 3 | 27 | 28 | 29 | 30 |

Testing Web Server

31 | 32 |

Static HTML

33 | 34 | 35 | 36 | 37 | 38 | 39 |
demo_1.htmldemo_2.htmlform_validation.html
40 |

PHP

41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 |

Python

52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
Python Demo 2CheckBox Demo
61 |

Perl

62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
Perl Form Test
73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /https_web_server/test/test.html: -------------------------------------------------------------------------------- 1 | 2 | HTTP Web Server Test 3 | 27 | 28 | 29 | 30 |

Testing Web Server

31 | 32 |

Static HTML

33 | 34 | 35 | 36 | 37 | 38 | 39 |
demo_1.htmldemo_2.htmlform_validation.html
40 |

PHP

41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 |

Python

52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
Python Demo 2CheckBox Demo
61 |

Perl

62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
Perl Form Test
73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /http_web_server/README.md: -------------------------------------------------------------------------------- 1 | 2 | # HTTP Web Server 3 | 4 | -------------------------------------------------------------------------------- 5 | 6 | # What Is It? 7 | 8 | An asynchronous tiny HTTP web server that supports HTTP web requests as well as 9 | PHP, CGI, Python, Perl etc. scripts execution on server sides. 10 | 11 | # Compilation 12 | 13 | ### Dependency 14 | 15 | ##### GCC with C++11 compiler(g++) & Boost C++ Library 16 | 17 | ### Step 1: Getting the Source Code 18 | 19 | You can download source by command: 20 | 21 | $ git clone https://github.com/pritamzope/http_web_server.git 22 | $ cd http_web_server 23 | 24 | or you can get source via other ways you prefer at 25 | 26 | ### Step 2: Compile and Run 27 | 28 | Run command: 29 | 30 | $ make 31 | $ ./httpserver 32 | 33 | 34 | ## Example 35 | 36 | Run server with directory path where all server files will be stored. 37 | e.g.: 38 | 39 | $ ./httpserver /home/pritam/resource 40 | 41 | Some testing examples are given in **test** directory.
42 | Copy the contents of **test** folder into /home/pritam/resource directory.
43 | Now open Web Browser and goto address **127.0.0.1:1234**.
44 | Server uses port 1234 by default which can be changed in source(main.cpp).
45 | It will show following server default page. 46 | 47 | 48 | 49 | That means server is started successfully and ready to serve clients. 50 | Many clients are given in **test** directory such as static HTML pages,PHP, CGI, Python and Perl scripts. 51 | 52 | Navigate your browser URL to **127.0.0.1:1234/test.html**, it will show following page for testing each web scripts.
53 | If server is running on another machine then use that machine's IP address. 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /https_web_server/README.md: -------------------------------------------------------------------------------- 1 | 2 | # HTTPS Web Server 3 | 4 | -------------------------------------------------------------------------------- 5 | 6 | # What Is It? 7 | 8 | An synchronous tiny HTTPS web server that supports HTTP web requests over Secure Socket Layer(SSL) as well as 9 | PHP, CGI, Python, Perl etc. scripts execution on server sides. 10 | 11 | # Compilation 12 | 13 | ### Dependency 14 | 15 | ##### GCC with C++11 compiler(g++) 16 | ##### Boost C++ Library 17 | ##### OpenSSL library 18 | 19 | ### Step 1: Getting the Source Code 20 | 21 | You can download source by command: 22 | 23 | $ git clone https://github.com/pritamzope/http_web_server.git 24 | $ cd http_web_server 25 | 26 | or you can get source via other ways you prefer at 27 | 28 | ### Step 2: Install OpenSSL 29 | 30 | I'm using Ubuntu, 31 | 32 | $ sudo apt-get install openssl 33 | 34 | or you can install manually by downloading source code from https://github.com/openssl/openssl 35 | 36 | ### Step 4: Generate OpenSSL Certificate and Key 37 | 38 | Run gen_openssl_key.sh script to generate HttpsWebServer.cert and HttpsWebServer.cert files. 39 | 40 | $ sh gen_openssl_key.sh 41 | 42 | following is the key generation example from my terminal:- 43 | 44 | pritam@pzope:~/http_web_server-master$ sh gen_openssl_key.sh 45 | Can't load /home/pritam/.rnd into RNG 46 | 140200119943616:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/pritam/.rnd 47 | Generating a RSA private key 48 | ......+++++ 49 | ..............+++++ 50 | writing new private key to 'HttpsWebServer.key' 51 | ----- 52 | You are about to be asked to enter information that will be incorporated 53 | into your certificate request. 54 | What you are about to enter is what is called a Distinguished Name or a DN. 55 | There are quite a few fields but you can leave some blank 56 | For some fields there will be a default value, 57 | If you enter '.', the field will be left blank. 58 | ----- 59 | Country Name (2 letter code) [AU]:IN 60 | State or Province Name (full name) [Some-State]:Maharashtra 61 | Locality Name (eg, city) []:Pune 62 | Organization Name (eg, company) [Internet Widgits Pty Ltd]:HttpsWebServer 63 | Organizational Unit Name (eg, section) []:HttpsWebServer 64 | Common Name (e.g. server FQDN or YOUR name) []:HttpsWebServer 65 | Email Address []: 66 | 67 | 68 | ### Step 5: Compile and Run 69 | 70 | For compiling source code with TLS/SSL library, i'm using header files location in makefile as '/usr/include/openssl/'. 71 | Run command: 72 | 73 | $ make 74 | $ ./httpsserver 75 | 76 | 77 | ## Example 78 | 79 | Run server with directory path where all server files will be stored. 80 | e.g.: 81 | 82 | $ ./httpsserver /home/pritam/resource 83 | 84 | Open browser and goto https://:1234/. remebmer https not http. 85 | Browser must be allowed to accept requests from this server. For example, Firefox browser will show following Warning, so go to "Advanced" option and click on "Accept the Risk and Continue". 86 | 87 | 88 | 89 | Some testing examples are given in **test** directory.
90 | Copy the contents of **test** folder into /home/pritam/resource directory.
91 | Now open Web Browser and goto address **127.0.0.1:1234**.
92 | Server uses port 1234 by default which can be changed in source(main.cpp).
93 | It will show following server default page. 94 | 95 | 96 | 97 | That means server is started successfully and ready to serve clients. 98 | Many clients are given in **test** directory such as static HTML pages,PHP, CGI, Python and Perl scripts. 99 | 100 | Navigate your browser URL to **127.0.0.1:1234/test.html**, it will show following page for testing each web scripts.
101 | If server is running on another machine then use that machine's IP address. 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /http_web_server/HttpService.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTPSERVICE_HPP 2 | #define HTTPSERVICE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | //http methods types 9 | enum class HttpMethods{ 10 | GET, 11 | HEAD, 12 | POST, 13 | PUT, 14 | DELETE, 15 | CONNECT, 16 | OPTIONS, 17 | TRACE, 18 | }; 19 | 20 | //each http request returned by parser 21 | struct HttpRequest 22 | { 23 | HttpMethods method; 24 | std::string request; 25 | std::string resource; 26 | std::string http_version; 27 | unsigned int status; 28 | }; 29 | 30 | 31 | class HttpRequestParser 32 | { 33 | public: 34 | HttpRequestParser(std::string&); 35 | std::shared_ptr GetHttpRequest(); 36 | 37 | private: 38 | std::string m_HttpRequest; 39 | }; 40 | 41 | 42 | class HttpService 43 | { 44 | public: 45 | HttpService(std::shared_ptr); 46 | 47 | void HttpHandleRequest(); 48 | 49 | private: 50 | std::string GetIp(); 51 | void ProcessGetRequest(); 52 | std::string GetCGIProgram(std::string); 53 | void ProcessPostRequest(); 54 | void ExecuteProgram(std::string, std::string); 55 | void ProcessHeadRequest(); 56 | void ProcessDeleteRequest(); 57 | void ProcessOptionsRequest(); 58 | std::string GetResponseStatus(); 59 | void SendResponse(); 60 | void Finish(); 61 | 62 | private: 63 | std::shared_ptr m_Socket; 64 | boost::asio::streambuf m_Request; 65 | std::string m_RequestedResource; 66 | std::unique_ptr m_ResourceBuffer; 67 | unsigned int m_ResponseStatusCode; 68 | std::size_t m_ResourceSizeInBytes; 69 | bool m_IsResponseSent; 70 | std::string m_ServerOptions; 71 | std::string m_ScriptData; 72 | std::string m_ContentType; 73 | 74 | 75 | const std::string m_DefaultIndexPage = "\n\nHTTP Web Server\ 76 | \n\n\n
\ 89 |
HTTP Web Server
\ 90 |
\ 91 |
It works!
\ 92 |

\ 93 | This is the default welcome page used to test the correct operation of the HTTP Web Server.\ 94 |
If you can read this page, it means that the HTTP Web server at this site is working properly.\ 95 |
You should replace this file (located at\ 96 | the provided path when server is started /index.html)\ 97 | before continuing to operate your HTTP server.\ 98 |
Request the static HTML page with Server IP and port 1234.\ 99 |
Request the PHP script and get it's the interpreted contents.\ 100 |


\ 102 | https://github.com/pritamzope/http_web_server/

\ 103 |
\n\n"; 104 | 105 | 106 | }; 107 | 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /https_web_server/HttpsService.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HTTPSERVICE_HPP 2 | #define HTTPSERVICE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef boost::asio::ssl::stream& SSLStream; 10 | 11 | //http methods types 12 | enum class HttpMethods{ 13 | GET, 14 | HEAD, 15 | POST, 16 | PUT, 17 | DELETE, 18 | CONNECT, 19 | OPTIONS, 20 | TRACE, 21 | }; 22 | 23 | //each http request returned by parser 24 | struct HttpRequest 25 | { 26 | HttpMethods method; 27 | std::string request; 28 | std::string resource; 29 | std::string http_version; 30 | unsigned int status; 31 | }; 32 | 33 | 34 | class HttpRequestParser 35 | { 36 | public: 37 | HttpRequestParser(std::string&); 38 | std::shared_ptr GetHttpRequest(); 39 | 40 | private: 41 | std::string m_HttpRequest; 42 | }; 43 | 44 | 45 | class HttpsService 46 | { 47 | public: 48 | HttpsService(); 49 | 50 | void HttpsHandleRequest(SSLStream); 51 | 52 | private: 53 | std::string GetIp(SSLStream); 54 | void ProcessGetRequest(SSLStream); 55 | std::string GetCGIProgram(std::string); 56 | void ProcessPostRequest(SSLStream); 57 | void ExecuteProgram(std::string, std::string); 58 | void ProcessHeadRequest(SSLStream); 59 | void ProcessDeleteRequest(SSLStream); 60 | void ProcessOptionsRequest(SSLStream); 61 | std::string GetResponseStatus(SSLStream); 62 | void SendResponse(SSLStream); 63 | void Finish(); 64 | 65 | private: 66 | boost::asio::streambuf m_Request; 67 | std::string m_RequestedResource; 68 | std::unique_ptr m_ResourceBuffer; 69 | unsigned int m_ResponseStatusCode; 70 | std::size_t m_ResourceSizeInBytes; 71 | bool m_IsResponseSent; 72 | std::string m_ServerOptions; 73 | std::string m_ScriptData; 74 | std::string m_ContentType; 75 | 76 | 77 | const std::string m_DefaultIndexPage = "\n\nHTTPS Web Server\ 78 | \n\n\n
\ 91 |
HTTPS Web Server
\ 92 |
\ 93 |
It works!
\ 94 |

\ 95 | This is the default welcome page used to test the correct operation of the HTTPS Web Server.\ 96 |
If you can read this page, it means that the HTTPS Web server at this site is working properly.\ 97 |
You should replace this file (located at\ 98 | the provided path when server is started /index.html)\ 99 | before continuing to operate your HTTPS server.\ 100 |
Request the static HTML page with Server IP and port 1234.\ 101 |
Request the PHP script and get it's the interpreted contents.\ 102 |


\ 104 | https://github.com/pritamzope/http_web_server/

\ 105 |
\n\n"; 106 | 107 | 108 | }; 109 | 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /http_web_server/test/html/form_validation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form validation 4 | 5 | 89 | 90 | 144 | 145 | 146 |

Student Form Validation

147 | 148 |
149 | 150 |
151 | 152 |
153 | 154 |

Personal Info:-

155 |

156 | First Name : 157 |
             158 |                 159 |     160 | Middle Name : 161 |
            162 |                  163 | Last Name : 164 | 165 |

166 | 167 | Permanent Address : 168 | 169 |
170 | 171 | Current Address :       172 | 173 |

174 | 175 | Permanent and Current Address is same ? 176 | 177 |
178 |
179 | 180 |
181 | 182 |

Qualification:-

183 |
184 | 185 | 191 | 192 |

193 | 194 | Graduation Percentage : 195 |
196 | Date of Birth :                 197 | 198 | 199 | 200 |
201 | 202 |
203 |
204 | 205 |
206 | 207 |
208 | 209 |
210 | 211 |
212 | 213 | 214 | -------------------------------------------------------------------------------- /https_web_server/test/html/form_validation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form validation 4 | 5 | 89 | 90 | 144 | 145 | 146 |

Student Form Validation

147 | 148 |
149 | 150 |
151 | 152 |
153 | 154 |

Personal Info:-

155 |

156 | First Name : 157 |
             158 |                 159 |     160 | Middle Name : 161 |
            162 |                  163 | Last Name : 164 | 165 |

166 | 167 | Permanent Address : 168 | 169 |
170 | 171 | Current Address :       172 | 173 |

174 | 175 | Permanent and Current Address is same ? 176 | 177 |
178 |
179 | 180 |
181 | 182 |

Qualification:-

183 |
184 | 185 | 191 | 192 |

193 | 194 | Graduation Percentage : 195 |
196 | Date of Birth :                 197 | 198 | 199 | 200 |
201 | 202 |
203 |
204 | 205 |
206 | 207 |
208 | 209 |
210 | 211 |
212 | 213 | 214 | -------------------------------------------------------------------------------- /https_web_server/HttpsService.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "HttpsService.hpp" 16 | 17 | using namespace boost; 18 | 19 | /* 20 | HTTP status codes 21 | for more status codes https://en.wikipedia.org/wiki/List_of_HTTP_status_codes 22 | */ 23 | std::unordered_map HttpStatusTable = 24 | { 25 | {101, "Switching Protocols"}, 26 | {201, "Created"}, 27 | {202, "Accepted"}, 28 | {200, "200 OK" }, 29 | {400, "Bad Request"}, 30 | {401, "Unauthorized"}, 31 | {404, "404 Not Found" }, 32 | {408, "Request Timeout"}, 33 | {413, "413 Request Entity Too Large" }, 34 | {500, "500 Internal Server Error" }, 35 | {501, "501 Not Implemented" }, 36 | {502, "Bad Gateway"}, 37 | {503, "Service Unavailable"}, 38 | {505, "505 HTTP Version Not Supported" } 39 | }; 40 | 41 | 42 | HttpRequestParser::HttpRequestParser(std::string& request): 43 | m_HttpRequest(request) 44 | {} 45 | 46 | //HTTP request parser, parses the request made by client 47 | //stores it into HttpRequest structure and return it 48 | std::shared_ptr HttpRequestParser::GetHttpRequest() 49 | { 50 | if(m_HttpRequest.empty()) return nullptr; 51 | 52 | std::string request_method, resource, http_version; 53 | std::istringstream request_line_stream(m_HttpRequest); 54 | //extract request method, GET, POST, ..... 55 | request_line_stream >> request_method; 56 | //extract requested resource 57 | request_line_stream >> resource; 58 | //extract HTTP version 59 | request_line_stream >> http_version; 60 | 61 | std::shared_ptr request(new HttpRequest); 62 | 63 | request->resource = std::move(resource); 64 | request->status = 0; 65 | 66 | if(request_method.compare("GET") == 0){ 67 | request->method = HttpMethods::GET; 68 | }else if(request_method.compare("HEAD") == 0){ 69 | request->method = HttpMethods::HEAD; 70 | }else if(request_method.compare("POST") == 0){ 71 | request->method = HttpMethods::POST; 72 | }else if(request_method.compare("PUT") == 0){ 73 | request->method = HttpMethods::PUT; 74 | }else if(request_method.compare("DELETE") == 0){ 75 | request->method = HttpMethods::DELETE; 76 | }else if(request_method.compare("CONNECT") == 0){ 77 | request->method = HttpMethods::CONNECT; 78 | }else if(request_method.compare("OPTIONS") == 0){ 79 | request->method = HttpMethods::OPTIONS; 80 | }else if(request_method.compare("TRACE") == 0){ 81 | request->method = HttpMethods::TRACE; 82 | }else{ 83 | request->status = 400; 84 | } 85 | 86 | if(http_version.compare("HTTP/1.1") == 0){ 87 | request->http_version = "1.1"; 88 | }else{ 89 | request->status = 505; 90 | } 91 | 92 | request->request = std::move(m_HttpRequest); 93 | 94 | return request; 95 | 96 | } 97 | 98 | 99 | extern std::string RESOURCE_DIRECTORY_PATH; 100 | 101 | HttpsService::HttpsService():m_Request(4096), m_IsResponseSent(false) 102 | {} 103 | 104 | 105 | //Handle each HTTP request made by client 106 | void HttpsService::HttpsHandleRequest(SSLStream ssl_stream) 107 | { 108 | try{ 109 | //perform TLS handshake 110 | ssl_stream.handshake(asio::ssl::stream_base::server); 111 | 112 | boost::asio::read_until(ssl_stream, m_Request, '\n'); 113 | 114 | //get string from read stream 115 | std::string request_line; 116 | std::istream request_stream(&m_Request); 117 | std::getline(request_stream, request_line, '\0'); 118 | 119 | 120 | //parse the string and get HTTP request 121 | HttpRequestParser parser(request_line); 122 | std::shared_ptr http_request = parser.GetHttpRequest(); 123 | 124 | std::cout<<"[ Handling Client: "<request; 127 | std::cout<<"}"<request); 130 | while(std::getline(istrstream, m_ScriptData)){} 131 | std::cout<<"ScriptData: "<status == 0){ 134 | m_RequestedResource = http_request->resource; 135 | //handle each method 136 | switch(http_request->method){ 137 | case HttpMethods::GET : 138 | ProcessGetRequest(ssl_stream); 139 | break; 140 | case HttpMethods::POST : 141 | ProcessPostRequest(ssl_stream); 142 | break; 143 | case HttpMethods::HEAD : 144 | ProcessHeadRequest(ssl_stream); 145 | break; 146 | case HttpMethods::DELETE : 147 | ProcessDeleteRequest(ssl_stream); 148 | break; 149 | case HttpMethods::OPTIONS : 150 | ProcessOptionsRequest(ssl_stream); 151 | break; 152 | default: break; 153 | } 154 | }else{ 155 | m_ResponseStatusCode = http_request->status; 156 | if(!m_IsResponseSent) 157 | SendResponse(ssl_stream); 158 | return; 159 | } 160 | }catch(system::system_error& ec) { 161 | std::cout<<"Error occured, Error code = "<(resource_fstream.tellg()); 225 | 226 | //read file into buffer 227 | m_ResourceBuffer.reset(new char[m_ResourceSizeInBytes]); 228 | resource_fstream.seekg(std::ifstream::beg); 229 | resource_fstream.read(m_ResourceBuffer.get(), m_ResourceSizeInBytes); 230 | 231 | //send response with file 232 | SendResponse(ssl_stream); 233 | 234 | } 235 | 236 | std::string HttpsService::GetCGIProgram(std::string resource_file) 237 | { 238 | std::ifstream in(resource_file, std::ios::in); 239 | char data[4096]; 240 | while(in.getline(data, 4096)){ 241 | std::string str(data); 242 | std::size_t find = str.find("#!"); 243 | if(find != std::string::npos){ 244 | std::string program = str.substr(find + 2, str.length() - 2); 245 | program.erase(remove_if(program.begin(), program.end(), isspace), program.end()); 246 | in.close(); 247 | return std::move(program); 248 | } 249 | } 250 | in.close(); 251 | return ""; 252 | } 253 | 254 | void HttpsService::ProcessPostRequest(SSLStream ssl_stream) 255 | { 256 | if(m_RequestedResource.empty()){ 257 | m_ResponseStatusCode = 400; 258 | return; 259 | } 260 | 261 | std::string resource_file_path = RESOURCE_DIRECTORY_PATH + m_RequestedResource; 262 | 263 | if(!boost::filesystem::exists(resource_file_path)){ 264 | m_ResponseStatusCode = 404; 265 | SendResponse(ssl_stream); 266 | return; 267 | } 268 | 269 | //if resource contains .php substring 270 | if(boost::contains(m_RequestedResource, ".php")){ 271 | //php command to execute 272 | std::string cmd = "php -f " + resource_file_path; 273 | 274 | //attach data to be passed to the script 275 | if(!m_ScriptData.empty()) 276 | cmd = cmd + " \"" + m_ScriptData + "\""; 277 | 278 | //a temporary file where output of an PHP interpretor will be stores 279 | std::string php_output_file = RESOURCE_DIRECTORY_PATH + std::string("/tempfile"); 280 | 281 | std::cout<<"Executing command: "< response_buffers; 289 | if(m_ResourceSizeInBytes > 0){ 290 | response_buffers.push_back(asio::buffer(m_ResourceBuffer.get(), m_ResourceSizeInBytes)); 291 | } 292 | 293 | //remove phpoutputfile 294 | std::remove(php_output_file.c_str()); 295 | 296 | SendResponse(ssl_stream); 297 | 298 | }else if(boost::contains(m_RequestedResource, ".cgi") || 299 | boost::contains(m_RequestedResource, ".py") || 300 | boost::contains(m_RequestedResource, ".pl")){ 301 | 302 | std::string program = GetCGIProgram(resource_file_path); 303 | 304 | //if program name is empty and requested source contain .py, 305 | //then set program to python3 by default 306 | if(program.empty() && boost::contains(m_RequestedResource, ".py")){ 307 | program = "/usr/bin/python3"; 308 | } 309 | 310 | if(program.empty() && boost::contains(m_RequestedResource, ".pl")){ 311 | program = "/usr/bin/perl"; 312 | } 313 | 314 | //cgi program command to execute on server 315 | std::string cmd = program + " " + resource_file_path; 316 | 317 | //attach data to be passed to the script 318 | if(!m_ScriptData.empty()) 319 | cmd = cmd + " \"" + m_ScriptData + "\""; 320 | 321 | std::cout<<"Executing command: "< response_buffers; 330 | if(m_ResourceSizeInBytes > 0){ 331 | response_buffers.push_back(asio::buffer(m_ResourceBuffer.get(), m_ResourceSizeInBytes)); 332 | } 333 | 334 | //remove outputfile 335 | std::remove(cgi_output_file.c_str()); 336 | 337 | SendResponse(ssl_stream); 338 | 339 | } 340 | } 341 | 342 | void HttpsService::ExecuteProgram(std::string command, std::string outputfile) 343 | { 344 | boost::process::system(command, boost::process::std_out > outputfile); 345 | 346 | //set requested resouce to outputfile for response to client 347 | m_RequestedResource = outputfile; 348 | 349 | //read outputfile contents 350 | std::ifstream resource_fstream(outputfile, std::ifstream::binary); 351 | 352 | if(!resource_fstream.is_open()){ 353 | m_ResponseStatusCode = 500; 354 | return; 355 | } 356 | 357 | m_ResponseStatusCode = 200; 358 | 359 | //find out file size 360 | resource_fstream.seekg(0, std::ifstream::end); 361 | m_ResourceSizeInBytes = static_cast(resource_fstream.tellg()); 362 | 363 | m_ResourceBuffer.reset(new char[m_ResourceSizeInBytes]); 364 | 365 | //read output file into resource buffer 366 | resource_fstream.seekg(std::ifstream::beg); 367 | resource_fstream.read(m_ResourceBuffer.get(), m_ResourceSizeInBytes); 368 | } 369 | 370 | //process head request by sending only headers not file 371 | void HttpsService::ProcessHeadRequest(SSLStream ssl_stream) 372 | { 373 | if(m_RequestedResource.empty()){ 374 | m_ResponseStatusCode = 400; 375 | SendResponse(ssl_stream); 376 | return; 377 | } 378 | 379 | std::string resource_file_path = RESOURCE_DIRECTORY_PATH + m_RequestedResource; 380 | 381 | if(!boost::filesystem::exists(resource_file_path)){ 382 | m_ResponseStatusCode = 404; 383 | SendResponse(ssl_stream); 384 | return; 385 | } 386 | 387 | std::ifstream resource_fstream(resource_file_path, std::ifstream::binary); 388 | 389 | if(!resource_fstream.is_open()){ 390 | m_ResponseStatusCode = 500; 391 | return; 392 | } 393 | 394 | resource_fstream.seekg(0, std::ifstream::end); 395 | m_ResourceSizeInBytes = static_cast(resource_fstream.tellg()); 396 | 397 | SendResponse(ssl_stream); 398 | 399 | } 400 | 401 | //delete the requested resource 402 | void HttpsService::ProcessDeleteRequest(SSLStream ssl_stream) 403 | { 404 | if(m_RequestedResource.empty()){ 405 | m_ResponseStatusCode = 400; 406 | SendResponse(ssl_stream); 407 | return; 408 | } 409 | 410 | std::string resource_file_path = RESOURCE_DIRECTORY_PATH + m_RequestedResource; 411 | 412 | if(!boost::filesystem::exists(resource_file_path)){ 413 | m_ResponseStatusCode = 404; 414 | SendResponse(ssl_stream); 415 | return; 416 | } 417 | 418 | std::remove(resource_file_path.c_str()); 419 | 420 | SendResponse(ssl_stream); 421 | 422 | } 423 | 424 | void HttpsService::ProcessOptionsRequest(SSLStream ssl_stream) 425 | { 426 | m_ServerOptions = "GET, POST, HEAD, DELETE, OPTIONS"; 427 | SendResponse(ssl_stream); 428 | } 429 | 430 | std::string HttpsService::GetResponseStatus(SSLStream ssl_stream) 431 | { 432 | std::string response_status; 433 | 434 | auto end = std::chrono::system_clock::now(); 435 | std::time_t end_time = std::chrono::system_clock::to_time_t(end); 436 | std::string timestr(std::ctime(&end_time)); 437 | 438 | ssl_stream.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_receive); 439 | 440 | auto status_line = HttpStatusTable[m_ResponseStatusCode]; 441 | 442 | response_status = std::string("HTTP/1.1 ") + status_line + "\n"; 443 | if(m_ResourceSizeInBytes > 0){ 444 | response_status += std::string("Content-Length: ") + 445 | std::to_string(m_ResourceSizeInBytes) + "\n"; 446 | } 447 | if(!m_ContentType.empty()){ 448 | response_status += m_ContentType + "\n"; 449 | }else{ 450 | response_status += std::string("Content-Type: text/html") + "\n"; 451 | } 452 | if(!m_ServerOptions.empty()){ 453 | response_status += std::string("Allow: ") + std::move(m_ServerOptions) + "\n"; 454 | } 455 | response_status += std::string("Server: TinyHttpWebServer/0.0.1") + "\n"; 456 | response_status += std::string("AcceptRanges: bytes") + "\n"; 457 | response_status += std::string("Connection: Closed") + "\n"; 458 | response_status += std::string("Date: ") + timestr + "\n"; 459 | 460 | return std::move(response_status); 461 | } 462 | 463 | void HttpsService::SendResponse(SSLStream ssl_stream) 464 | { 465 | std::vector response_buffers; 466 | 467 | m_IsResponseSent = true; 468 | 469 | std::string response_status = GetResponseStatus(ssl_stream); 470 | response_buffers.push_back(asio::buffer(std::move(response_status))); 471 | 472 | if(m_ResourceSizeInBytes > 0){ 473 | response_buffers.push_back(asio::buffer(m_ResourceBuffer.get(), m_ResourceSizeInBytes)); 474 | } 475 | 476 | //send response to client with data 477 | boost::asio::write(ssl_stream, response_buffers); 478 | 479 | m_ResourceSizeInBytes = 0; 480 | 481 | } 482 | 483 | void HttpsService::Finish() 484 | { 485 | delete this; 486 | } 487 | 488 | 489 | -------------------------------------------------------------------------------- /http_web_server/HttpService.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "HttpService.hpp" 15 | 16 | using namespace boost; 17 | 18 | /* 19 | HTTP status codes 20 | for more status codes https://en.wikipedia.org/wiki/List_of_HTTP_status_codes 21 | */ 22 | std::unordered_map HttpStatusTable = 23 | { 24 | {101, "Switching Protocols"}, 25 | {201, "Created"}, 26 | {202, "Accepted"}, 27 | {200, "200 OK" }, 28 | {400, "Bad Request"}, 29 | {401, "Unauthorized"}, 30 | {404, "404 Not Found" }, 31 | {408, "Request Timeout"}, 32 | {413, "413 Request Entity Too Large" }, 33 | {500, "500 Internal Server Error" }, 34 | {501, "501 Not Implemented" }, 35 | {502, "Bad Gateway"}, 36 | {503, "Service Unavailable"}, 37 | {505, "505 HTTP Version Not Supported" } 38 | }; 39 | 40 | 41 | HttpRequestParser::HttpRequestParser(std::string& request): 42 | m_HttpRequest(request) 43 | {} 44 | 45 | //HTTP request parser, parses the request made by client 46 | //stores it into HttpRequest structure and return it 47 | std::shared_ptr HttpRequestParser::GetHttpRequest() 48 | { 49 | if(m_HttpRequest.empty()) return nullptr; 50 | 51 | std::string request_method, resource, http_version; 52 | std::istringstream request_line_stream(m_HttpRequest); 53 | //extract request method, GET, POST, ..... 54 | request_line_stream >> request_method; 55 | //extract requested resource 56 | request_line_stream >> resource; 57 | //extract HTTP version 58 | request_line_stream >> http_version; 59 | 60 | std::shared_ptr request(new HttpRequest); 61 | 62 | request->resource = std::move(resource); 63 | request->status = 0; 64 | 65 | if(request_method.compare("GET") == 0){ 66 | request->method = HttpMethods::GET; 67 | }else if(request_method.compare("HEAD") == 0){ 68 | request->method = HttpMethods::HEAD; 69 | }else if(request_method.compare("POST") == 0){ 70 | request->method = HttpMethods::POST; 71 | }else if(request_method.compare("PUT") == 0){ 72 | request->method = HttpMethods::PUT; 73 | }else if(request_method.compare("DELETE") == 0){ 74 | request->method = HttpMethods::DELETE; 75 | }else if(request_method.compare("CONNECT") == 0){ 76 | request->method = HttpMethods::CONNECT; 77 | }else if(request_method.compare("OPTIONS") == 0){ 78 | request->method = HttpMethods::OPTIONS; 79 | }else if(request_method.compare("TRACE") == 0){ 80 | request->method = HttpMethods::TRACE; 81 | }else{ 82 | request->status = 400; 83 | } 84 | 85 | if(http_version.compare("HTTP/1.1") == 0){ 86 | request->http_version = "1.1"; 87 | }else{ 88 | request->status = 505; 89 | } 90 | 91 | request->request = std::move(m_HttpRequest); 92 | 93 | return request; 94 | 95 | } 96 | 97 | 98 | extern std::string RESOURCE_DIRECTORY_PATH; 99 | 100 | 101 | HttpService::HttpService(std::shared_ptr sock): 102 | m_Socket(sock), 103 | m_Request(4096), 104 | m_IsResponseSent(false) 105 | { 106 | } 107 | 108 | //Handle each HTTP request made by client 109 | void HttpService::HttpHandleRequest() 110 | { 111 | //read the request from client 112 | asio::async_read_until(*m_Socket.get(), 113 | m_Request, '\r', 114 | [this](const boost::system::error_code& ec, 115 | std::size_t bytes_transfered) 116 | { 117 | //get string from read stream 118 | std::string request_line; 119 | std::istream request_stream(&m_Request); 120 | std::getline(request_stream, request_line, '\0'); 121 | 122 | //parse the string and get HTTP request 123 | HttpRequestParser parser(request_line); 124 | std::shared_ptr http_request = parser.GetHttpRequest(); 125 | 126 | std::cout<<"[ Handling Client: "<request; 129 | std::cout<<"}"<request); 132 | while(std::getline(istrstream, m_ScriptData)){} 133 | std::cout<<"ScriptData: "<status == 0){ 136 | m_RequestedResource = http_request->resource; 137 | //handle each method 138 | switch(http_request->method){ 139 | case HttpMethods::GET : 140 | ProcessGetRequest(); 141 | break; 142 | case HttpMethods::POST : 143 | ProcessPostRequest(); 144 | break; 145 | case HttpMethods::HEAD : 146 | ProcessHeadRequest(); 147 | break; 148 | case HttpMethods::DELETE : 149 | ProcessDeleteRequest(); 150 | break; 151 | case HttpMethods::OPTIONS : 152 | ProcessOptionsRequest(); 153 | break; 154 | default: break; 155 | } 156 | }else{ 157 | m_ResponseStatusCode = http_request->status; 158 | if(!m_IsResponseSent) 159 | SendResponse(); 160 | return; 161 | } 162 | 163 | }); 164 | } 165 | 166 | //returns ip address of connected endpoint 167 | std::string HttpService::GetIp() 168 | { 169 | asio::ip::tcp::endpoint ep = m_Socket->remote_endpoint(); 170 | asio::ip::address addr = ep.address(); 171 | return std::move(addr.to_string()); 172 | } 173 | 174 | //process the client GET request with requested resource 175 | void HttpService::ProcessGetRequest() 176 | { 177 | //if resource is empty then status code = 400 178 | if(m_RequestedResource.empty()){ 179 | m_ResponseStatusCode = 400; 180 | SendResponse(); 181 | return; 182 | } 183 | 184 | //if resource contains PHP script, then send it to POST request 185 | //for preprocessing php script 186 | if(boost::contains(m_RequestedResource, ".php") || 187 | boost::contains(m_RequestedResource, ".cgi") || 188 | boost::contains(m_RequestedResource, ".py") || 189 | boost::contains(m_RequestedResource, ".pl")){ 190 | ProcessPostRequest(); 191 | return; 192 | } 193 | 194 | //if resource is null or / then set resource to index.html file 195 | if(m_RequestedResource.compare("/") == 0){ 196 | m_RequestedResource = std::string("/index.html"); 197 | std::ofstream outfile(RESOURCE_DIRECTORY_PATH + m_RequestedResource, std::ios::out); 198 | outfile<(resource_fstream.tellg()); 225 | 226 | //read file into buffer 227 | m_ResourceBuffer.reset(new char[m_ResourceSizeInBytes]); 228 | resource_fstream.seekg(std::ifstream::beg); 229 | resource_fstream.read(m_ResourceBuffer.get(), m_ResourceSizeInBytes); 230 | 231 | //send response with file 232 | SendResponse(); 233 | 234 | } 235 | 236 | std::string HttpService::GetCGIProgram(std::string resource_file) 237 | { 238 | std::ifstream in(resource_file, std::ios::in); 239 | char data[4096]; 240 | while(in.getline(data, 4096)){ 241 | std::string str(data); 242 | std::size_t find = str.find("#!"); 243 | if(find != std::string::npos){ 244 | std::string program = str.substr(find + 2, str.length() - 2); 245 | program.erase(remove_if(program.begin(), program.end(), isspace), program.end()); 246 | in.close(); 247 | return std::move(program); 248 | } 249 | } 250 | in.close(); 251 | return ""; 252 | } 253 | 254 | void HttpService::ProcessPostRequest() 255 | { 256 | if(m_RequestedResource.empty()){ 257 | m_ResponseStatusCode = 400; 258 | return; 259 | } 260 | 261 | std::string resource_file_path = RESOURCE_DIRECTORY_PATH + m_RequestedResource; 262 | 263 | if(!boost::filesystem::exists(resource_file_path)){ 264 | m_ResponseStatusCode = 404; 265 | SendResponse(); 266 | return; 267 | } 268 | 269 | //if resource contains .php substring 270 | if(boost::contains(m_RequestedResource, ".php")){ 271 | //php command to execute 272 | std::string cmd = "php -f " + resource_file_path; 273 | 274 | //attach data to be passed to the script 275 | if(!m_ScriptData.empty()) 276 | cmd = cmd + " \"" + m_ScriptData + "\""; 277 | 278 | //a temporary file where output of an PHP interpretor will be stores 279 | std::string php_output_file = RESOURCE_DIRECTORY_PATH + std::string("/tempfile"); 280 | 281 | std::cout<<"Executing command: "< response_buffers; 289 | if(m_ResourceSizeInBytes > 0){ 290 | response_buffers.push_back(asio::buffer(m_ResourceBuffer.get(), m_ResourceSizeInBytes)); 291 | } 292 | 293 | //remove phpoutputfile 294 | std::remove(php_output_file.c_str()); 295 | 296 | SendResponse(); 297 | 298 | }else if(boost::contains(m_RequestedResource, ".cgi") || 299 | boost::contains(m_RequestedResource, ".py") || 300 | boost::contains(m_RequestedResource, ".pl")){ 301 | 302 | std::string program = GetCGIProgram(resource_file_path); 303 | 304 | //if program name is empty and requested source contain .py, 305 | //then set program to python3 by default 306 | if(program.empty() && boost::contains(m_RequestedResource, ".py")){ 307 | program = "/usr/bin/python3"; 308 | } 309 | 310 | if(program.empty() && boost::contains(m_RequestedResource, ".pl")){ 311 | program = "/usr/bin/perl"; 312 | } 313 | 314 | //cgi program command to execute on server 315 | std::string cmd = program + " " + resource_file_path; 316 | 317 | //attach data to be passed to the script 318 | if(!m_ScriptData.empty()) 319 | cmd = cmd + " \"" + m_ScriptData + "\""; 320 | 321 | std::cout<<"Executing command: "< response_buffers; 330 | if(m_ResourceSizeInBytes > 0){ 331 | response_buffers.push_back(asio::buffer(m_ResourceBuffer.get(), m_ResourceSizeInBytes)); 332 | } 333 | 334 | //remove outputfile 335 | std::remove(cgi_output_file.c_str()); 336 | 337 | SendResponse(); 338 | 339 | } 340 | } 341 | 342 | void HttpService::ExecuteProgram(std::string command, std::string outputfile) 343 | { 344 | boost::process::system(command, boost::process::std_out > outputfile); 345 | 346 | //set requested resouce to outputfile for response to client 347 | m_RequestedResource = outputfile; 348 | 349 | //read outputfile contents 350 | std::ifstream resource_fstream(outputfile, std::ifstream::binary); 351 | 352 | if(!resource_fstream.is_open()){ 353 | m_ResponseStatusCode = 500; 354 | return; 355 | } 356 | 357 | m_ResponseStatusCode = 200; 358 | 359 | //find out file size 360 | resource_fstream.seekg(0, std::ifstream::end); 361 | m_ResourceSizeInBytes = static_cast(resource_fstream.tellg()); 362 | 363 | m_ResourceBuffer.reset(new char[m_ResourceSizeInBytes]); 364 | 365 | //read output file into resource buffer 366 | resource_fstream.seekg(std::ifstream::beg); 367 | resource_fstream.read(m_ResourceBuffer.get(), m_ResourceSizeInBytes); 368 | } 369 | 370 | //process head request by sending only headers not file 371 | void HttpService::ProcessHeadRequest() 372 | { 373 | if(m_RequestedResource.empty()){ 374 | m_ResponseStatusCode = 400; 375 | SendResponse(); 376 | return; 377 | } 378 | 379 | std::string resource_file_path = RESOURCE_DIRECTORY_PATH + m_RequestedResource; 380 | 381 | if(!boost::filesystem::exists(resource_file_path)){ 382 | m_ResponseStatusCode = 404; 383 | SendResponse(); 384 | return; 385 | } 386 | 387 | std::ifstream resource_fstream(resource_file_path, std::ifstream::binary); 388 | 389 | if(!resource_fstream.is_open()){ 390 | m_ResponseStatusCode = 500; 391 | return; 392 | } 393 | 394 | resource_fstream.seekg(0, std::ifstream::end); 395 | m_ResourceSizeInBytes = static_cast(resource_fstream.tellg()); 396 | 397 | SendResponse(); 398 | 399 | } 400 | 401 | //delete the requested resource 402 | void HttpService::ProcessDeleteRequest() 403 | { 404 | if(m_RequestedResource.empty()){ 405 | m_ResponseStatusCode = 400; 406 | SendResponse(); 407 | return; 408 | } 409 | 410 | std::string resource_file_path = RESOURCE_DIRECTORY_PATH + m_RequestedResource; 411 | 412 | if(!boost::filesystem::exists(resource_file_path)){ 413 | m_ResponseStatusCode = 404; 414 | SendResponse(); 415 | return; 416 | } 417 | 418 | std::remove(resource_file_path.c_str()); 419 | 420 | SendResponse(); 421 | 422 | } 423 | 424 | void HttpService::ProcessOptionsRequest() 425 | { 426 | m_ServerOptions = "GET, POST, HEAD, DELETE, OPTIONS"; 427 | SendResponse(); 428 | } 429 | 430 | std::string HttpService::GetResponseStatus() 431 | { 432 | std::string response_status; 433 | 434 | auto end = std::chrono::system_clock::now(); 435 | std::time_t end_time = std::chrono::system_clock::to_time_t(end); 436 | std::string timestr(std::ctime(&end_time)); 437 | 438 | m_Socket->shutdown(asio::ip::tcp::socket::shutdown_receive); 439 | 440 | auto status_line = HttpStatusTable[m_ResponseStatusCode]; 441 | 442 | response_status = std::string("HTTP/1.1 ") + status_line + "\n"; 443 | if(m_ResourceSizeInBytes > 0){ 444 | response_status += std::string("Content-Length: ") + 445 | std::to_string(m_ResourceSizeInBytes) + "\n"; 446 | } 447 | if(!m_ContentType.empty()){ 448 | response_status += m_ContentType + "\n"; 449 | }else{ 450 | response_status += std::string("Content-Type: text/html") + "\n"; 451 | } 452 | if(!m_ServerOptions.empty()){ 453 | response_status += std::string("Allow: ") + std::move(m_ServerOptions) + "\n"; 454 | } 455 | response_status += std::string("Server: TinyHttpWebServer/0.0.1") + "\n"; 456 | response_status += std::string("AcceptRanges: bytes") + "\n"; 457 | response_status += std::string("Connection: Closed") + "\n"; 458 | response_status += std::string("Date: ") + timestr + "\n"; 459 | 460 | return std::move(response_status); 461 | } 462 | 463 | void HttpService::SendResponse() 464 | { 465 | std::vector response_buffers; 466 | 467 | m_IsResponseSent = true; 468 | 469 | std::string response_status = GetResponseStatus(); 470 | response_buffers.push_back(asio::buffer(std::move(response_status))); 471 | 472 | if(m_ResourceSizeInBytes > 0){ 473 | response_buffers.push_back(asio::buffer(m_ResourceBuffer.get(), m_ResourceSizeInBytes)); 474 | } 475 | 476 | //send response to client with data 477 | asio::async_write(*m_Socket.get(), 478 | response_buffers, 479 | [this](const boost::system::error_code& ec, 480 | std::size_t bytes_transferred) 481 | { 482 | if(ec != 0){ 483 | std::cout<<"Error occured, Error code = "<