getMimeTypes(void);
11 | std::string getFormattedTime(void);
--------------------------------------------------------------------------------
/root/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 404
8 |
9 |
10 |
11 |
18 |
25 |
26 |
37 |
38 | Page Not Found
39 |
40 |
--------------------------------------------------------------------------------
/root/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Webserv - 505
7 |
8 |
9 | Machine is down
10 |
11 |
--------------------------------------------------------------------------------
/root/cgi-bin/cookie.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | # Import modules for CGI handling
4 | import cgi, cgitb , html , os
5 | # cgitb.enable()
6 | # parse the HTTP cookies sent by the client
7 | cookie_string = os.environ.get('HTTP_COOKIE')
8 |
9 | if cookie_string:
10 | index = cookie_string.find("=")
11 | color = cookie_string[index+1:]
12 | else:
13 | color = "white"
14 | # access the value of a cookie named "my_cookie"
15 | # Create instance of FieldStorage
16 | form = cgi.FieldStorage()
17 |
18 | # Get data from fields
19 | # Get data from fields
20 | # if form.getvalue('HTTP_COOKIE'):
21 | # color = cookie_string
22 | if form.getvalue('color') and form.getvalue('color') != color:
23 | color = form.getvalue('color')
24 |
25 |
26 | import time
27 | current_date = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
28 | time_to_live = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime(time.time() + 60))
29 | print("Set-Cookie:color=%s;expires=%s\r\n" % (color, time_to_live))
30 | print("Content-Type:text/html\r\n")
31 | print("Server:Python HTTP Server\r\n")
32 | print("\r\n\r\n")
33 | bodystring = "" % (color)
34 | bodystring += "Welcome to my page
"
35 | bodystring += ""
40 | bodystring += ""
41 | bodystring += ""
42 | print (bodystring)
--------------------------------------------------------------------------------
/root/cgi-bin/login.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgi
4 | import cgitb
5 | import os
6 | import base64
7 | import hashlib
8 | import sys
9 |
10 | # Enable error traceback for debugging (comment this out in production)
11 | cgitb.enable()
12 |
13 | # Define a dictionary to store user credentials (replace with a database in production)
14 | user_credentials = {
15 | 'admin': 'admin',
16 | 'user2': 'password2',
17 | }
18 |
19 | # Function to hash a string using MD5
20 | def md5_hash(text):
21 | return hashlib.md5(text.encode()).hexdigest()
22 |
23 | # Function to encode a session ID as Base64
24 | def encode_session_id(username, password):
25 | session_id = "{}:{}".format(username, password)
26 | return base64.b64encode(session_id.encode()).decode()
27 |
28 | # Function to set a session cookie with the Base64-encoded session ID
29 | def set_session_cookie(session_id):
30 | print("Set-Cookie: session_id={}".format(session_id))
31 |
32 | # Function to get the session ID from cookies
33 | def get_session_id():
34 | cookies = os.environ.get("HTTP_COOKIE")
35 | if cookies:
36 | cookies = cookies.split("; ")
37 | for cookie in cookies:
38 | # split the cookie and ignore the = in the value of the cookie
39 | name, value = cookie.split("=", 1)
40 | if name == "session_id":
41 | return value
42 | return None
43 |
44 | # Function to decode a session ID from Base64
45 | def decode_session_id(session_id):
46 | decoded_session_id = base64.b64decode(session_id).decode()
47 | return decoded_session_id.split(":")
48 |
49 | # Function to check if a user is logged in
50 | def is_user_logged_in(session_id):
51 | return session_id is not None
52 |
53 | # Main CGI script
54 | def main():
55 | # Get the session ID from cookies or generate a new one
56 | session_id = get_session_id()
57 | if not session_id:
58 | session_id = None
59 | username = None
60 | password = None
61 |
62 | if session_id:
63 | # Decode session ID
64 | decoded_session_id = decode_session_id(session_id)
65 | if len(decoded_session_id) == 2:
66 | username, password = decoded_session_id
67 | if username not in user_credentials or user_credentials[username] != password:
68 | username = None
69 |
70 | # HTML content
71 | print("Content-Type: text/html\r\n")
72 | form = cgi.FieldStorage()
73 |
74 | if "username" in form and "password" in form:
75 | username = form.getvalue("username")
76 | password = form.getvalue("password")
77 | if username in user_credentials and user_credentials[username] == password:
78 | # Encode username and password as the session ID
79 | session_id = encode_session_id(username, password)
80 | set_session_cookie(session_id)
81 | print("\r\n\r\n")
82 | print("Login Page with CGI and Sessions")
83 | print("Login Page with CGI and Sessions
")
84 |
85 |
86 | if is_user_logged_in(session_id):
87 | print("You are logged in as {}.
".format(username))
88 | print("")
91 | print("""""")
92 | else:
93 | print("")
100 |
101 | print("")
102 |
103 | if __name__ == "__main__":
104 | main()
105 |
--------------------------------------------------------------------------------
/root/cgi-bin/logout.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgi
4 | import os
5 |
6 | # Function to clear the session cookie
7 | def clear_session_cookie():
8 | print("Set-Cookie:session_id=\r\n")
9 |
10 | # Main CGI script
11 | def main():
12 | # Clear the session cookie to log the user out
13 | clear_session_cookie()
14 |
15 | # HTML content
16 | print("Content-Type: text/html\r\n")
17 | print("\r\n\r\n")
18 | print("Logout")
19 | print("Logout
")
20 | print("You have been logged out.
")
21 | print("Return to Login Page")
22 | print("")
23 |
24 | if __name__ == "__main__":
25 | main()
26 |
--------------------------------------------------------------------------------
/root/cgi-bin/multi_get.py:
--------------------------------------------------------------------------------
1 |
2 | import cgi
3 | form = cgi.FieldStorage()
4 |
5 |
6 | if 'm' in form and 'n' in form:
7 | try:
8 | multiplicand1 = int(form['m'].value)
9 | multiplicand2 = int(form['n'].value)
10 | product = multiplicand1 * multiplicand2
11 | print("")
12 | print("Multiplication Result
")
13 | print(multiplicand1)
14 | print("*")
15 | print(multiplicand2)
16 | print("=")
17 | print(product)
18 | print("")
19 | except ValueError:
20 | print("")
21 | print("Error
")
22 | print("Invalid input. Please enter valid numbers.
")
23 | print("")
24 | else:
25 | print("")
26 | print("Error
")
27 | print("Missing parameters. Please provide both multiplicands.
")
28 | print("")
29 |
30 |
--------------------------------------------------------------------------------
/root/cgi-bin/post.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'cgi'
4 |
5 | cgi = CGI.new
6 |
7 | # Function to generate a random number within a range
8 | def generate_random_number(min, max)
9 | rand(min..max)
10 | end
11 |
12 | # Process the form data if it's a POST request
13 | if cgi.request_method == "POST"
14 | min_range = cgi.params['min_range'][0].to_i
15 | max_range = cgi.params['max_range'][0].to_i
16 | random_number = generate_random_number(min_range, max_range)
17 |
18 | # Set the content type to HTML
19 | cgi.out("text/html") do
20 | "
21 |
22 | Random Number Generator
23 |
24 |
25 | Random Number:
26 | #{random_number}
27 | Generate Another Random Number
28 |
29 | "
30 | end
31 | else
32 | # Set the content type to HTML
33 | cgi.out("text/html") do
34 | "
35 |
36 | Random Number Generator
37 |
38 |
39 | Generate Random Number
40 |
47 |
48 | "
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/root/cgi-bin/post_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import cgi
3 | # while True:
4 | # continue
5 | form = cgi.FieldStorage()
6 | if form.getvalue('textcontent'):
7 | text_content = form.getvalue('textcontent')
8 | else:
9 | text_content = "Not entered"
10 | body_string = """Hello - Second CGI Program %s
""" % (text_content)
11 |
12 | print (body_string)
--------------------------------------------------------------------------------
/root/cgi-bin/random.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'cgi'
4 |
5 | cgi = CGI.new
6 |
7 | # Generate a random number between 1 and 100
8 | random_number = rand(1..100)
9 |
10 | # Set the content type to HTML
11 | cgi.out("text/html") do
12 | "
13 |
14 | Random Number Generator
15 |
16 |
17 | Random Number:
18 | #{random_number}
19 |
20 | "
21 | end
22 |
--------------------------------------------------------------------------------
/root/css/404.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Audiowide&display=swap');
2 |
3 | html,
4 | body{
5 | margin: 0px;
6 | overflow: hidden;
7 | }
8 |
9 | div{
10 | position: absolute;
11 | top: 0%;
12 | left: 0%;
13 | height: 100%;
14 | width: 100%;
15 | margin: 0px;
16 | background: radial-gradient(circle, #240015 0%, #12000b 100%);
17 | overflow: hidden;
18 | }
19 |
20 | .wrap{
21 | position: absolute;
22 | left: 50%;
23 | top: 50%;
24 | transform: translate(-50%, -50%);
25 | }
26 |
27 | h2{
28 | position: absolute;
29 | top: 50%;
30 | left: 50%;
31 | margin-top: 150px;
32 | font-size: 32px;
33 | text-transform: uppercase;
34 | transform: translate(-50%, -50%);
35 | display: block;
36 | color: #12000a;
37 | font-weight: 300;
38 | font-family: Audiowide;
39 | text-shadow: 0px 0px 4px #12000a;
40 | animation: fadeInText 3s ease-in 3.5s forwards, flicker4 5s linear 7.5s infinite, hueRotate 6s ease-in-out 3s infinite;
41 | }
42 |
43 | #svgWrap_1,
44 | #svgWrap_2{
45 | position: absolute;
46 | height: auto;
47 | width: 600px;
48 | max-width: 100%;
49 | top: 50%;
50 | left: 50%;
51 | transform: translate(-50%, -50%);
52 | }
53 |
54 | #svgWrap_1,
55 | #svgWrap_2,
56 | div{
57 | animation: hueRotate 6s ease-in-out 3s infinite;
58 | }
59 |
60 | #id1_1,
61 | #id2_1,
62 | #id3_1{
63 | stroke: #ff005d;
64 | stroke-width: 3px;
65 | fill: transparent;
66 | filter: url(#glow);
67 | }
68 |
69 | #id1_2,
70 | #id2_2,
71 | #id3_2{
72 | stroke: #12000a;
73 | stroke-width: 3px;
74 | fill: transparent;
75 | filter: url(#glow);
76 | }
77 |
78 | #id3_1{
79 | stroke-dasharray: 940px;
80 | stroke-dashoffset: -940px;
81 | animation: drawLine3 2.5s ease-in-out 0s forwards, flicker3 4s linear 4s infinite;
82 | }
83 |
84 | #id2_1{
85 | stroke-dasharray: 735px;
86 | stroke-dashoffset: -735px;
87 | animation: drawLine2 2.5s ease-in-out 0.5s forwards, flicker2 4s linear 4.5s infinite;
88 | }
89 |
90 | #id1_1{
91 | stroke-dasharray: 940px;
92 | stroke-dashoffset: -940px;
93 | animation: drawLine1 2.5s ease-in-out 1s forwards, flicker1 4s linear 5s infinite;
94 | }
95 |
96 | @keyframes drawLine1 {
97 | 0% {stroke-dashoffset: -940px;}
98 | 100%{stroke-dashoffset: 0px;}
99 | }
100 |
101 | @keyframes drawLine2 {
102 | 0% {stroke-dashoffset: -735px;}
103 | 100%{stroke-dashoffset: 0px;}
104 | }
105 |
106 | @keyframes drawLine3 {
107 | 0% {stroke-dashoffset: -940px;}
108 | 100%{stroke-dashoffset: 0px;}
109 | }
110 |
111 | @keyframes flicker1{
112 | 0% {stroke: #ff005d;}
113 | 1% {stroke: transparent;}
114 | 3% {stroke: transparent;}
115 | 4% {stroke: #ff005d;}
116 | 6% {stroke: #ff005d;}
117 | 7% {stroke: transparent;}
118 | 13% {stroke: transparent;}
119 | 14% {stroke: #ff005d;}
120 | 100%{stroke: #ff005d;}
121 | }
122 |
123 | @keyframes flicker2{
124 | 0% {stroke: #ff005d;}
125 | 50% {stroke: #ff005d;}
126 | 51% {stroke: transparent;}
127 | 61% {stroke: transparent;}
128 | 62% {stroke: #ff005d;}
129 | 100%{stroke: #ff005d;}
130 | }
131 |
132 | @keyframes flicker3{
133 | 0% {stroke: #ff005d;}
134 | 1% {stroke: transparent;}
135 | 10% {stroke: transparent;}
136 | 11% {stroke: #ff005d;}
137 | 40% {stroke: #ff005d;}
138 | 41% {stroke: transparent;}
139 | 45% {stroke: transparent;}
140 | 46% {stroke: #ff005d;}
141 | 100%{stroke: #ff005d;}
142 | }
143 |
144 | @keyframes flicker4{
145 | 0% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
146 | 30% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
147 | 31% {color: #12000a;text-shadow:0px 0px 4px #12000a;}
148 | 32% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
149 | 36% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
150 | 37% {color: #12000a;text-shadow:0px 0px 4px #12000a;}
151 | 41% {color: #12000a;text-shadow:0px 0px 4px #12000a;}
152 | 42% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
153 | 85% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
154 | 86% {color: #12000a;text-shadow:0px 0px 4px #12000a;}
155 | 95% {color: #12000a;text-shadow:0px 0px 4px #12000a;}
156 | 96% {color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
157 | 100%{color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
158 | }
159 |
160 | @keyframes fadeInText{
161 | 1% {color: #12000a;text-shadow:0px 0px 4px #12000a;}
162 | 70% {color: #ff005d;text-shadow:0px 0px 14px #ff005d;}
163 | 100%{color: #ff005d;text-shadow:0px 0px 4px #ff005d;}
164 | }
165 |
166 | @keyframes hueRotate{
167 | 0% {
168 | filter: hue-rotate(0deg);
169 | }
170 | 50% {
171 | filter: hue-rotate(-120deg);
172 | }
173 | 100% {
174 | filter: hue-rotate(0deg);
175 | }
176 | }
--------------------------------------------------------------------------------
/root/css/styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Globals
3 | */
4 |
5 | /* Links */
6 | a,
7 | a:focus,
8 | a:hover {
9 | color: #fff;
10 | }
11 |
12 | /* Custom default button */
13 | .btn-secondary,
14 | .btn-secondary:hover,
15 | .btn-secondary:focus {
16 | color: #333;
17 | text-shadow: none; /* Prevent inheritance from `body` */
18 | background-color: #fff;
19 | border: .05rem solid #fff;
20 | }
21 |
22 |
23 | /*
24 | * Base structure
25 | */
26 |
27 | html,
28 | body {
29 | height: 100%;
30 | background-image: url("../img/bg.png");
31 | background-position: center;
32 | background-repeat: no-repeat;
33 | background-size: cover;
34 | }
35 |
36 | body {
37 | display: -ms-flexbox;
38 | display: -webkit-box;
39 | display: flex;
40 | -ms-flex-pack: center;
41 | -webkit-box-pack: center;
42 | justify-content: center;
43 | color: #fff;
44 | text-shadow: 0 .05rem .1rem rgba(0, 0, 0, .5);
45 | box-shadow: inset 0 0 5rem rgba(0, 0, 0, .5);
46 | }
47 |
48 | .cover-container {
49 | max-width: 42em;
50 | }
51 |
52 |
53 | /*
54 | * Header
55 | */
56 | .masthead {
57 | margin-bottom: 2rem;
58 | }
59 |
60 | .masthead-brand {
61 | margin-bottom: 0;
62 | }
63 |
64 | .nav-masthead .nav-link {
65 | padding: .25rem 0;
66 | font-weight: 700;
67 | color: rgba(255, 255, 255, .5);
68 | background-color: transparent;
69 | border-bottom: .25rem solid transparent;
70 | }
71 |
72 | .nav-masthead .nav-link:hover,
73 | .nav-masthead .nav-link:focus {
74 | border-bottom-color: rgba(255, 255, 255, .25);
75 | }
76 |
77 | .nav-masthead .nav-link + .nav-link {
78 | margin-left: 1rem;
79 | }
80 |
81 | .nav-masthead .active {
82 | color: #fff;
83 | border-bottom-color: #fff;
84 | }
85 |
86 | @media (min-width: 48em) {
87 | .masthead-brand {
88 | float: left;
89 | }
90 | .nav-masthead {
91 | float: right;
92 | }
93 | }
94 |
95 |
96 | /*
97 | * Cover
98 | */
99 | .cover {
100 | padding: 0 1.5rem;
101 | }
102 | .cover .btn-lg {
103 | padding: .75rem 1.25rem;
104 | font-weight: 700;
105 | }
106 |
107 |
108 | /*
109 | * Footer
110 | */
111 | .mastfoot {
112 | color: rgba(255, 255, 255, .5);
113 | }
114 |
--------------------------------------------------------------------------------
/root/errors/404.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zmoussam/http-server/e5e106c4dcd865c3464690e6efaebb12322d3ba5/root/errors/404.html
--------------------------------------------------------------------------------
/root/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zmoussam/http-server/e5e106c4dcd865c3464690e6efaebb12322d3ba5/root/img/bg.png
--------------------------------------------------------------------------------
/root/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Webserv - A simple web server
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
24 | Webserv
25 | A simple web server with support for CGI technology similar to market ready web servers
26 |
27 |
28 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/root/msmen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Msemen
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/root2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 | hey
10 |
11 |
--------------------------------------------------------------------------------
/src/CGI/CGIHandler.cpp:
--------------------------------------------------------------------------------
1 | # include "CGIHandler.hpp"
2 | CGI::CGI()
3 | {
4 | _root = "www/";
5 | _redirect = "";
6 | _cgi_path = "";
7 | _compiler = "";
8 | _headersSent = false;
9 | _error_code = 0;
10 | _status_code = "";
11 | _isCgiDone = false;
12 | _cgiRan = false;
13 | _fd = 0;
14 | _pid = 0;
15 | _status = 0;
16 | }
17 |
18 | CGI::CGI(int clientSocket, std::vector &servers) : _servers(servers)
19 | {
20 | _root = "www/";
21 | _redirect = "";
22 | _cgi_path = "";
23 | _compiler = "";
24 | _clientSocket = clientSocket;
25 | _headersSent = false;
26 | _error_code = 0;
27 | _status_code = "";
28 | _isCgiDone = false;
29 | _cgiRan = false;
30 | _fd = 0;
31 | _cookies = std::vector();
32 | }
33 |
34 | CGI::~CGI()
35 | {
36 |
37 | }
38 | std::string getFileExtension(const std::string& filename)
39 | {
40 | size_t dotPosition = filename.find_last_of(".");
41 |
42 | if (dotPosition != std::string::npos) {
43 | return filename.substr(dotPosition);
44 | }
45 | return "";
46 | }
47 |
48 | void CGI::initHeaders() {
49 | _headers["Server"] = "luna/1.0";
50 | _headers["Content-Type"] = "text/html";
51 | if (!(_redirect.empty())) {
52 | _headers["Location"] = _redirect;
53 | _error_code = 301;
54 | }
55 | _headers["Connection"] = "keep-alive";
56 | }
57 |
58 | void CGI::findConfig(Request &req)
59 | {
60 | const std::map &headers = req.getHeaders();
61 | if (headers.find("Host") != headers.end())
62 | {
63 | int port = req.getPort();
64 | std::string host = headers.find("Host")->second;
65 | for (std::vector::iterator it = _servers.begin(); it != _servers.end(); it++)
66 | {
67 | if (it->getString(SERVER_NAME) == host || it->getString(SERVER_NAME) + ":" + std::to_string(port) == host) {
68 | _config = *it;
69 | return;
70 | }
71 | }
72 | }
73 | _config = _servers[0];
74 | }
75 |
76 | int CGI::initializeCGIParameters(Request &req, Response &resp) {
77 | findConfig(req);
78 | _cgi_path = req.getPath();
79 | _filename = _cgi_path.substr(_cgi_path.find_last_of('/') + 1);
80 | std::string fileExtension = "";
81 | std::vector::iterator it = _config.location.begin();
82 | std::string extension = getFileExtension(_filename);
83 | if (extension.empty())
84 | {
85 | _error_code = 500;
86 | return 2;
87 | }
88 | while (it != resp._config.location.end())
89 | {
90 | if (it->getLocationName().empty())
91 | {
92 | _error_code = 500;
93 | return 2;
94 | }
95 | std::string cgiLocation = it->getLocationName().substr(1);
96 | if (cgiLocation == extension && it->getLocationName()[0] == '*')
97 | {
98 | _methods = it->getMethods();
99 | if (!it->getString(ROOT).empty())
100 | _root = it->getString(ROOT) + (it->getString(ROOT).back() != '/' ? "/" : "");
101 | _compiler = it->getCompiler();
102 | _redirect = it->getReturned();
103 | break;
104 | }
105 | else if (it == resp._config.location.end())
106 | {
107 | _error_code = 500;
108 | return 2;
109 | }
110 | it++;
111 | }
112 | std::vector::iterator it_meth = _methods.begin();
113 | while (it_meth != _methods.end())
114 | {
115 | if (*it_meth == req.getMethod())
116 | break;
117 | it_meth++;
118 | }
119 | if (it_meth == _methods.end())
120 | {
121 | _error_code = 405;
122 | return 2;
123 | }
124 | if (_compiler.empty())
125 | {
126 | _error_code = 500;
127 | return 2;
128 | }
129 | initHeaders();
130 | return 0;
131 | }
132 |
133 | std::vector CGI::getCookies() const
134 | {
135 | return _cookies;
136 | }
137 |
138 | std::string CGI::parseCookies(std::string cookies)
139 | {
140 | std::string setCookie = "Set-Cookie:";
141 | std::string::size_type pos = cookies.find(setCookie);
142 | while (pos != std::string::npos)
143 | {
144 | std::string::size_type pos2 = cookies.find("\r\n", pos);
145 | if (pos2 != std::string::npos)
146 | {
147 | std::string value = cookies.substr(pos + setCookie.length(), pos2 - pos - setCookie.length());
148 | _cookies.push_back(value);
149 | cookies.erase(pos, pos2 - pos);
150 | }
151 | pos = cookies.find(setCookie, pos2 + 2);
152 | }
153 | return cookies;
154 | }
155 |
156 | void CGI::parseHeaders(std::string headers)
157 | {
158 | std::string headerKey[5] = {"Content-Type", "Content-Length", "Location", "Server", "Connection"};
159 | for (int i = 0; i < 5; i++)
160 | {
161 | if (headers.find(headerKey[i]) != std::string::npos)
162 | {
163 | std::string::size_type pos = headers.find(headerKey[i]);
164 | std::string::size_type pos2 = headers.find("\r\n", pos);
165 | std::string value = headers.substr(pos + headerKey[i].length() + 1, pos2 - pos - headerKey[i].length() - 1);
166 | _headers[headerKey[i]] = value;
167 | }
168 | }
169 | }
170 |
171 | int CGI::handlePostMethod(Request &req) {
172 | if (req.getMethod() == "POST")
173 | {
174 | std::string tmpfile = std::to_string(getpid()) + ".txt";
175 | std::string body = req.getBody();
176 | std::ofstream ofs(tmpfile);
177 | if (!ofs.is_open()) {
178 | _error_code = E500;
179 | return -1;
180 | }
181 | ofs << body;
182 | ofs.close();
183 | int fdf = open(tmpfile.c_str(), O_RDWR);
184 | if (fdf == -1) {
185 | _error_code = E500;
186 | return -1;
187 | }
188 | if (dup2(fdf, STDIN_FILENO) == -1) {
189 | _error_code = E500;
190 | return -1;
191 | }
192 | close(fdf);
193 | if (req.getHeaders().find("Content-Length") != req.getHeaders().end())
194 | setenv("CONTENT_LENGTH", req.getHeaders()["Content-Length"].c_str(), 1);
195 | unlink(tmpfile.c_str());
196 | }
197 | return 0;
198 | }
199 |
200 | int CGI::executeCGIScript(int clientSocket) {
201 | unused(clientSocket);
202 | std::string path = _root + _filename;
203 | if (access(path.c_str(), F_OK) == -1) {
204 | _error_code = E404;
205 | return -1;
206 | }
207 | std::string command = _compiler + _root + _filename;
208 | FILE* pipe = popen(command.c_str(), "r");
209 | if (!pipe) {
210 | _error_code = E500;
211 | return -1;
212 | }
213 |
214 | char buffer[255];
215 | std::string body;
216 | while (fgets(buffer, sizeof(buffer), pipe) != NULL)
217 | body += buffer;
218 | std::string headers[] = {"Content-Type", "Content-Length", "Location", "Set-Cookie", "Server", "Connection"};
219 | for (int i = 0; i < 6; i++)
220 | {
221 | if (body.find("\r\n\r\n") != std::string::npos && body.find(headers[i]) != std::string::npos)
222 | {
223 | std::string::size_type pos = body.find("\r\n\r\n");
224 | if (pos != std::string::npos)
225 | {
226 | int fd = open(COOKIFILE, O_RDWR | O_CREAT | O_TRUNC, 0666);
227 | if (fd == -1) {
228 | _error_code = E500;
229 | return -1;
230 | }
231 | std::string tmp = body.substr(0, pos);
232 | tmp += "\r\n\r\n";
233 | tmp += '\0';
234 | if (write(fd, tmp.c_str(), tmp.size()) <= 0) {
235 | _error_code = E500;
236 | return -1;
237 | }
238 | close(fd);
239 | body.erase(0, pos + 4);
240 | }
241 | break;
242 | }
243 | }
244 | pclose(pipe);
245 |
246 | if (_fd != -1) {
247 | if (write(_fd, body.c_str(), body.size()) <= 0)
248 | {
249 | _error_code = E500;
250 | return -1;
251 | }
252 | } else {
253 | _error_code = E500;
254 | return -1;
255 | }
256 | return 0;
257 | }
258 | int CGI::CGIHandler(Request &req, Response &resp, int clientSocket)
259 | {
260 | if (_cgiRan == false) {
261 | int random = rand();
262 | _cgifd = "/tmp/" + std::to_string(random) + ".cgi";
263 | _pid = fork();
264 | int res = initializeCGIParameters(req, resp);
265 | if (res != 0)
266 | {
267 | _isCgiDone = true;
268 | return (res);
269 | }
270 | _fd = open(_cgifd.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
271 | if (_pid == 0) {
272 | if (handlePostMethod(req) != 0)
273 | exit (_error_code);
274 | if (req.getCookies().size() > 0)
275 | setenv("HTTP_COOKIE", req.getCookies().c_str(), 1);
276 | std::map headers = req.getHeaders();
277 | for (std::map::iterator it = headers.begin(); it != headers.end(); it++)
278 | {
279 | if (it->first == "Content-Length")
280 | continue;
281 | std::string key = HTTP_ENV + it->first;
282 | std::transform(key.begin(), key.end(), key.begin(), ::toupper);
283 | setenv(key.c_str(), it->second.c_str(), 1);
284 | }
285 | setenv("REQUEST_METHOD", req.getMethod().c_str(), 1);
286 | setenv("REQUEST_URI", req.getPath().c_str(), 1);
287 | setenv("QUERY_STRING", req.getQueries().c_str(), 1);
288 | setenv("SCRIPT_NAME", req.getPath().c_str(), 1);
289 | setenv("GATEWAY_INTERF", "CGI/1.1", 1);
290 | setenv("PATH_INFO", req.getPath().c_str(), 1);
291 | setenv("SCRIPT_NAME", _cgi_path.c_str(), 1);
292 | setenv("REDIRECT_STATUS", "", 1);
293 | if (executeCGIScript(clientSocket) != 0)
294 | exit (_error_code);
295 | exit(0);
296 | }
297 | }
298 | if (waitpid(_pid, &_status, WNOHANG) == -1)
299 | {
300 | if (WIFEXITED(_status)) {
301 | _status = WEXITSTATUS(_status);
302 | (_error_code = RESET_ERROR_CODE + _status) && _error_code == RESET_ERROR_CODE ? _error_code = 0 : _error_code;
303 | _isCgiDone = true;
304 | if (_fd == -1)
305 | _error_code = 500;
306 | int fd = open(COOKIFILE, O_RDONLY);
307 | if (access(COOKIFILE, F_OK) != -1)
308 | {
309 | if (fd == -1)
310 | _error_code = 500;
311 | char buffer[128];
312 | std::string body = "";
313 | while (read(fd, buffer, sizeof(buffer) - 1) > 0){
314 | buffer[sizeof(buffer) - 1] = '\0';
315 | body += buffer;
316 | }
317 | body = parseCookies(body);
318 | parseHeaders(body);
319 | close(fd);
320 | unlink(COOKIFILE);
321 | }
322 | }
323 | else
324 | return (CONTINUE);
325 | }
326 | _cgiRan = true;
327 | return (CONTINUE);
328 | }
329 |
--------------------------------------------------------------------------------
/src/Config/Config.cpp:
--------------------------------------------------------------------------------
1 | #include"Config.hpp"
2 |
3 | Config::Config() {}
4 | Config::Config(char *file)
5 | {
6 | this->_file = file;
7 | }
8 | Config::~Config() {}
9 | ServerConf::ServerConf() {
10 | this->_autoindex = false;
11 | this->_bodySize = 100000;
12 | this->_host = "127.0.0.1";
13 | this->_serverName = "";
14 | this->_listen = 8000;
15 | this->_root = "";
16 | this->_index = "";
17 | this->_uploadPath = "upload/";
18 | this->_errorPages = std::map();
19 | this->_methods = std::vector();
20 | }
21 | ServerConf::~ServerConf() {}
22 | Location::Location() {
23 | this->_locationName = "";
24 | this->_returned = "";
25 | this->_compiler = "";
26 | }
27 | Location::~Location() {}
28 |
29 | ServerConf::ServerConf(const ServerConf ©)
30 | {
31 | this->_autoindex = copy._autoindex;
32 | this->_bodySize = copy._bodySize;
33 | this->_host = copy._host;
34 | this->_serverName = copy._serverName;
35 | this->_listen = copy._listen;
36 | this->_root = copy._root;
37 | this->_index = copy._index;
38 | this->_errorPages = copy._errorPages;
39 | this->location = copy.location;
40 | this->_methods = copy._methods;
41 | this->_uploadPath = copy._uploadPath;
42 | }
43 |
44 | ServerConf &ServerConf::operator=(const ServerConf ©)
45 | {
46 | this->_autoindex = copy._autoindex;
47 | this->_bodySize = copy._bodySize;
48 | this->_host = copy._host;
49 | this->_serverName = copy._serverName;
50 | this->_listen = copy._listen;
51 | this->_root = copy._root;
52 | this->_index = copy._index;
53 | this->_errorPages = copy._errorPages;
54 | this->location = copy.location;
55 | this->_methods = copy._methods;
56 | this->_uploadPath = copy._uploadPath;
57 | return *this;
58 | }
59 |
60 | void Location::setReturned(std::string returned)
61 | {
62 | this->_returned = returned;
63 | }
64 |
65 | void Location::setCompiler(std::string compiler)
66 | {
67 | this->_compiler = compiler + " ";
68 | }
69 |
70 | std::string Location::getCompiler() const
71 | {
72 | return this->_compiler;
73 | }
74 |
75 | std::string Location::getReturned() const
76 | {
77 | return this->_returned;
78 | }
79 |
80 |
81 | void ServerConf::setMethods(std::vector methods)
82 | {
83 | this->_methods = methods;
84 | }
85 |
86 | std::vector ServerConf::getMethods() const
87 | {
88 | return this->_methods;
89 | }
90 | std::string Location::getLocationName() const
91 | {
92 | return this->_locationName;
93 | }
94 |
95 | bool ServerConf::getAutoindex() const
96 | {
97 | return this->_autoindex;
98 | }
99 |
100 | void ServerConf::setAutoindex(bool autoindex)
101 | {
102 | this->_autoindex = autoindex;
103 | }
104 |
105 | void Location::setLocationName(std::string locationName)
106 | {
107 | this->_locationName = locationName;
108 | }
109 |
110 | void ServerConf::setErrorPage(std::map errorPage) {
111 | this->_errorPages = errorPage;
112 | }
113 |
114 | std::map ServerConf::getErrorPages() const {
115 | return this->_errorPages;
116 | }
117 |
118 | void ServerConf::setString(std::string type, std::string value)
119 | {
120 | if (type == "server_name")
121 | this->_serverName = value;
122 | else if (type == "host")
123 | this->_host = value;
124 | else if (type == "root")
125 | this->_root = value;
126 | else if (type == "index")
127 | this->_index = value;
128 | else if (type == "upload_path")
129 | this->_uploadPath = value;
130 | }
131 |
132 | void ServerConf::setNum(std::string type, size_t value)
133 | {
134 | if (type == "listen")
135 | this->_listen = value;
136 | else if (type == "client_body_size")
137 | this->_bodySize = value;
138 | }
139 |
140 | std::string ServerConf::getString(std::string type) const
141 | {
142 | if (type == "server_name")
143 | return this->_serverName;
144 | else if (type == "host")
145 | return this->_host;
146 | else if (type == "root")
147 | return this->_root;
148 | else if (type == "index")
149 | return this->_index;
150 | else if (type == "upload_path")
151 | {
152 | if (this->_root[this->_root.length() - 1] != '/' && this->_uploadPath[0] != '/')
153 | return (this->_root + "/" + this->_uploadPath);
154 | else if (this->_root[this->_root.length() - 1] == '/' && this->_uploadPath[0] == '/')
155 | return (this->_root + this->_uploadPath.substr(1));
156 | else
157 | return (this->_root + this->_uploadPath);
158 | }
159 | return "";
160 | }
161 |
162 | size_t ServerConf::getNum(std::string type) const
163 | {
164 | if (type == "listen")
165 | return this->_listen;
166 | else if (type == "client_body_size")
167 | return this->_bodySize;
168 | return 0;
169 | }
170 |
--------------------------------------------------------------------------------
/src/Config/Parser.cpp:
--------------------------------------------------------------------------------
1 | #include"Parser.hpp"
2 |
3 | Parser::Parser() {
4 | index = 0;
5 | }
6 | Parser::~Parser() {}
7 | bool Parser::look(std::string type) {
8 | if (index > tokens.size())
9 | return false;
10 | if (index == tokens.size())
11 | return false;
12 | return tokens[index]._type == type;
13 | }
14 |
15 | Token Parser::match(std::string type) {
16 | if (look(type)) {
17 | return tokens[index++];
18 | }
19 | throw std::runtime_error(UNEXPECTED_TOKEN);
20 | }
21 |
22 | bool checkQuotes(std::string &line)
23 | {
24 | size_t openQuote = std::count(line.begin(), line.end(), '"');
25 | return(openQuote % 2 == 0);
26 | }
27 |
28 | size_t parseNum(std::string str) {
29 | size_t number = 0;
30 | if (str.empty())
31 | throw std::runtime_error(NOT_VALID);
32 | for (size_t i = 0; i < str.length(); i++) {
33 | if (isdigit(str[i]))
34 | number = number * 10 + (str[i] - '0');
35 | else
36 | throw std::runtime_error(NOT_VALID);
37 | }
38 | if (number > INT_MAX)
39 | throw std::runtime_error(BIG_NUMBER);
40 | return number;
41 | }
42 |
43 | std::string parseValue(std::string str) {
44 | std::string res;
45 | if (str[0] == '"' && checkQuotes(str)) {
46 | res = str.substr(1, str.size() - 2);
47 | }
48 | else
49 | throw std::runtime_error(NOT_VALID);
50 | return res;
51 | }
52 |
53 | std::vector splitArgs(std::string value)
54 | {
55 | std::vector args;
56 | size_t quoteStartPos = 0;
57 | size_t quoteEndPos = value.find('"', quoteStartPos + 1);
58 | size_t count = std::count(value.begin(), value.end(), ',');
59 | while (true)
60 | {
61 | if (count == 0 && value.find(',', quoteEndPos + 1) != std::string::npos)
62 | throw std::runtime_error(NOT_VALID);
63 | if (quoteStartPos != std::string::npos && quoteEndPos != std::string::npos)
64 | {
65 | std::string arg = value.substr(quoteStartPos + 1, quoteEndPos - quoteStartPos - 1);
66 | if (arg.find(' ') != std::string::npos || arg.empty()) {
67 | throw std::runtime_error(NOT_VALID);
68 | }
69 | args.push_back(arg);
70 | quoteStartPos = value.find('"', quoteEndPos + 1);
71 | quoteEndPos = value.find('"', quoteStartPos + 1);
72 | }
73 | if (count == 0)
74 | break;
75 | count--;
76 | }
77 | return args;
78 | }
79 |
80 | std::map Parser::parseErrorPage() {
81 | match("error_page");
82 | std::map errorPage;
83 | std::vector args;
84 |
85 | args = splitArgs(match("value")._value);
86 | if (args.size() % 2 != 0)
87 | throw std::runtime_error(NOT_VALID);
88 | for (size_t i = 0; i < args.size(); i++) {
89 | size_t code = parseNum(args[i++]);
90 | std::string path = args[i];
91 | errorPage[code] = path;
92 | }
93 | return errorPage;
94 | }
95 |
96 |
97 | std::string Parser::parseReturned()
98 | {
99 | match("return");
100 | std::string returned = parseValue(match("value")._value);
101 | return returned;
102 | }
103 |
104 | bool Parser::parseAutoindex()
105 | {
106 | match("autoindex");
107 | bool autoindex;
108 | std::string boolstr = parseValue(match("value")._value);
109 | if (boolstr == "on")
110 | autoindex = true;
111 | else if (boolstr == "off")
112 | autoindex = false;
113 | else
114 | throw std::runtime_error(NOT_VALID);
115 | return autoindex;
116 | }
117 |
118 | std::vector Parser::parseMethods()
119 | {
120 | match("allow_methods");
121 | std::vector methods;
122 | std::vector args;
123 |
124 | args = splitArgs(match("value")._value);
125 | for (size_t i = 0; i < args.size(); i++) {
126 | if ((std::find(methods.begin(), methods.end(), args[i]) != methods.end() || (args[i] != "GET" && args[i] != "POST" && args[i] != "DELETE")))
127 | throw std::runtime_error(NOT_VALID);
128 | methods.push_back(args[i]);
129 | }
130 | return methods;
131 | }
132 |
133 | size_t Parser::parseNumRules(std::string key) {
134 | match(key);
135 | size_t bodySize = parseNum(parseValue(match("value")._value));
136 | return bodySize;
137 | }
138 |
139 | std::string Parser::parseStringRules(std::string key) {
140 | match(key);
141 | std::string value = parseValue(match("value")._value);
142 | return value;
143 | }
144 |
145 | Location Parser::parseLocationBody()
146 | {
147 | Location location;
148 | bool isCGI = false;
149 |
150 | location.setLocationName(parseValue(match("value")._value));
151 | if (location.getLocationName()[0] == '*')
152 | isCGI = true;
153 | match("{");
154 | while (!look("}")) {
155 | if (look("allow_methods"))
156 | location.setMethods(parseMethods());
157 | else if (look("root"))
158 | location.setString(ROOT, parseStringRules(ROOT));
159 | else if (look("index") && isCGI == false)
160 | location.setString(INDEX, parseStringRules(INDEX));
161 | else if (look("client_body_size"))
162 | location.setNum(BODY_SIZE, parseNumRules(BODY_SIZE));
163 | else if (look("error_page") && isCGI == false)
164 | location.setErrorPage(parseErrorPage());
165 | else if (look("autoindex") && isCGI == false)
166 | location.setAutoindex(parseAutoindex());
167 | else if (look("return"))
168 | location.setReturned(parseStringRules(REDIRECT));
169 | else if (look("compiler"))
170 | location.setCompiler(parseStringRules(COMPILER));
171 | else if (look("upload_path"))
172 | location.setString(UPLOAD_PATH, parseUploadPath(parseStringRules(UPLOAD_PATH)));
173 | else
174 | throw std::runtime_error(UNEXPECTED_TOKEN);
175 | }
176 | return location;
177 | }
178 |
179 | Location Parser::parseLocation() {
180 | Location location;
181 | match("location");
182 | location = parseLocationBody();
183 | match("}");
184 | return location;
185 | }
186 |
187 | std::string Parser::parseUploadPath(std::string value) {
188 | if (value[0] != '/')
189 | value = "/" + value;
190 | if (value[value.size() - 1] != '/')
191 | value += "/";
192 | return value;
193 | }
194 |
195 | ServerConf Parser::parseServerBody() {
196 | ServerConf server;
197 | while (!look("}")) {
198 | if (look("listen"))
199 | server.setNum(LISTEN, parseNumRules(LISTEN));
200 | else if (look("host"))
201 | server.setString(HOST, parseStringRules(HOST));
202 | else if (look("server_name"))
203 | server.setString(SERVER_NAME, parseStringRules(SERVER_NAME));
204 | else if (look("root"))
205 | server.setString(ROOT, parseStringRules(ROOT));
206 | else if (look("index"))
207 | server.setString(INDEX, parseStringRules(INDEX));
208 | else if (look("client_body_size"))
209 | server.setNum(BODY_SIZE, parseNumRules(BODY_SIZE));
210 | else if (look("error_page"))
211 | server.setErrorPage(parseErrorPage());
212 | else if (look("autoindex"))
213 | server.setAutoindex(parseAutoindex());
214 | else if (look("allow_methods"))
215 | server.setMethods(parseMethods());
216 | else if (look("upload_path"))
217 | server.setString(UPLOAD_PATH, parseUploadPath(parseStringRules(UPLOAD_PATH)));
218 | else if (look("location"))
219 | server.location.push_back(parseLocation());
220 | else
221 | throw std::runtime_error(UNEXPECTED_TOKEN);
222 | }
223 | return server;
224 | }
225 |
226 | ServerConf Parser::parseServer() {
227 | match("server");
228 | match("{");
229 | ServerConf server = parseServerBody();
230 | match("}");
231 | if (!look("server") && index != tokens.size())
232 | throw std::runtime_error(UNEXPECTED_TOKEN);
233 | return server;
234 | }
235 |
236 | void Parser::parseConfig(Config &config) {
237 | if (look("server"))
238 | {
239 | while (look("server"))
240 | {
241 | if (look("server"))
242 | config._servers.push_back(parseServer());
243 | else
244 | throw std::runtime_error(UNEXPECTED_TOKEN);
245 | }
246 | }
247 | else
248 | throw std::runtime_error(UNEXPECTED_TOKEN);
249 | }
250 |
251 | std::string parseKey(std::string token)
252 | {
253 | std::string key;
254 | for (size_t i = 0; i < token.size(); ++i) {
255 | if (token[i] == ':') {
256 | key = token.substr(0, i);
257 | break;
258 | }
259 | }
260 | return key;
261 | }
262 |
263 | void fillTokens(std::string line, Token tokens, std::vector &tokenArr)
264 | {
265 | std::istringstream iss(line);
266 | std::string token;
267 | while (iss >> token) {
268 | if (token[0] == '#')
269 | break;
270 | else if (!token.empty() && (!token.compare("server") || !token.compare("{") || !token.compare("}") || !token.compare("location"))) {
271 | tokens._type = token;
272 | tokens._value = token;
273 | }
274 | else if (!token.empty() && token[token.length() - 1] == ':')
275 | {
276 | tokens._type = parseKey(token);
277 | tokens._value = token;
278 | }
279 | else if (!token.empty() && token[0] == '"' && checkQuotes(token))
280 | {
281 | tokens._type = "value";
282 | tokens._value = token;
283 | }
284 | else
285 | throw std::runtime_error(NOT_VALID);
286 | tokenArr.push_back(tokens);
287 | }
288 | }
289 |
290 | void Tokenizer(std::vector &tokens, char *fileName)
291 | {
292 | std::ifstream file(fileName);
293 | std::string line;
294 | Token token;
295 | if (file.is_open()) {
296 | while (getline(file, line)) {
297 | size_t start_pos = line.find_first_not_of(" \t");
298 | if (start_pos != std::string::npos)
299 | line = line.substr(start_pos);
300 | if (!line.empty())
301 | fillTokens(line, token, tokens);
302 | }
303 | file.close();
304 | }
305 | else
306 | throw std::runtime_error(FAIL_OPEN);
307 | }
308 |
309 | void Parser::parseToken(Config &config)
310 | {
311 | try {
312 | Tokenizer(this->tokens, config._file);
313 | parseConfig(config);
314 | while (config._servers.size() > 1) {
315 | for (size_t i = 0; i < config._servers.size() - 1; i++) {
316 | for (size_t j = i + 1; j < config._servers.size(); j++) {
317 | if (config._servers[i].getNum(LISTEN) == config._servers[j].getNum(LISTEN) && config._servers[i].getString(SERVER_NAME) == config._servers[j].getString(SERVER_NAME))
318 | throw std::runtime_error("ERROR: SAME_SERVER");
319 | }
320 | }
321 | break;
322 | }
323 | for (size_t i = 0; i < config._servers.size(); i++) {
324 | config._serversByPort[config._servers[i].getNum(LISTEN)].push_back(config._servers[i]);
325 | }
326 | } catch (std::exception &e) {
327 | std::cout << e.what() << std::endl;
328 | exit(1);
329 | }
330 | }
331 | void parsefile(Config &config)
332 | {
333 | Parser parse;
334 | parse.parseToken(config);
335 | }
336 |
--------------------------------------------------------------------------------
/src/Utils/Utils.cpp:
--------------------------------------------------------------------------------
1 | # include "Utils.hpp"
2 |
3 | std::map getMimeTypes(void)
4 | {
5 | std::map mimeTypes;
6 |
7 | mimeTypes[".323"] = "text/h323";
8 | mimeTypes[".3g2"] = "video/3gpp2";
9 | mimeTypes[".3gp"] = "video/3gpp";
10 | mimeTypes[".3gp2"] = "video/3gpp2";
11 | mimeTypes[".3gpp"] = "video/3gpp";
12 | mimeTypes[".7z"] = "application/x-7z-compressed";
13 | mimeTypes[".aa"] = "audio/audible";
14 | mimeTypes[".AAC"] = "audio/aac";
15 | mimeTypes[".aaf"] = "application/octet-stream";
16 | mimeTypes[".aax"] = "audio/vnd.audible.aax";
17 | mimeTypes[".ac3"] = "audio/ac3";
18 | mimeTypes[".aca"] = "application/octet-stream";
19 | mimeTypes[".accda"] = "application/msaccess.addin";
20 | mimeTypes[".accdb"] = "application/msaccess";
21 | mimeTypes[".accdc"] = "application/msaccess.cab";
22 | mimeTypes[".accde"] = "application/msaccess";
23 | mimeTypes[".accdr"] = "application/msaccess.runtime";
24 | mimeTypes[".accdt"] = "application/msaccess";
25 | mimeTypes[".accdw"] = "application/msaccess.webapplication";
26 | mimeTypes[".accft"] = "application/msaccess.ftemplate";
27 | mimeTypes[".acx"] = "application/internet-property-stream";
28 | mimeTypes[".AddIn"] = "text/xml";
29 | mimeTypes[".ade"] = "application/msaccess";
30 | mimeTypes[".adobebridge"] = "application/x-bridge-url";
31 | mimeTypes[".adp"] = "application/msaccess";
32 | mimeTypes[".ADT"] = "audio/vnd.dlna.adts";
33 | mimeTypes[".ADTS"] = "audio/aac";
34 | mimeTypes[".afm"] = "application/octet-stream";
35 | mimeTypes[".ai"] = "application/postscript";
36 | mimeTypes[".aif"] = "audio/aiff";
37 | mimeTypes[".aifc"] = "audio/aiff";
38 | mimeTypes[".aiff"] = "audio/aiff";
39 | mimeTypes[".air"] = "application/vnd.adobe.air-application-installer-package+zip";
40 | mimeTypes[".amc"] = "application/mpeg";
41 | mimeTypes[".anx"] = "application/annodex";
42 | mimeTypes[".apk"] = "application/vnd.android.package-archive";
43 | mimeTypes[".apng"] = "image/apng";
44 | mimeTypes[".application"] = "application/x-ms-application";
45 | mimeTypes[".art"] = "image/x-jg";
46 | mimeTypes[".asa"] = "application/xml";
47 | mimeTypes[".asax"] = "application/xml";
48 | mimeTypes[".ascx"] = "application/xml";
49 | mimeTypes[".asd"] = "application/octet-stream";
50 | mimeTypes[".asf"] = "video/x-ms-asf";
51 | mimeTypes[".ashx"] = "application/xml";
52 | mimeTypes[".asi"] = "application/octet-stream";
53 | mimeTypes[".asm"] = "text/plain";
54 | mimeTypes[".asmx"] = "application/xml";
55 | mimeTypes[".aspx"] = "application/xml";
56 | mimeTypes[".asr"] = "video/x-ms-asf";
57 | mimeTypes[".asx"] = "video/x-ms-asf";
58 | mimeTypes[".atom"] = "application/atom+xml";
59 | mimeTypes[".au"] = "audio/basic";
60 | mimeTypes[".avci"] = "image/avci";
61 | mimeTypes[".avcs"] = "image/avcs";
62 | mimeTypes[".avi"] = "video/x-msvideo";
63 | mimeTypes[".avif"] = "image/avif";
64 | mimeTypes[".avifs"] = "image/avif-sequence";
65 | mimeTypes[".axa"] = "audio/annodex";
66 | mimeTypes[".axs"] = "application/olescript";
67 | mimeTypes[".axv"] = "video/annodex";
68 | mimeTypes[".bas"] = "text/plain";
69 | mimeTypes[".bcpio"] = "application/x-bcpio";
70 | mimeTypes[".bin"] = "application/octet-stream";
71 | mimeTypes[".bmp"] = "image/bmp";
72 | mimeTypes[".c"] = "text/plain";
73 | mimeTypes[".cab"] = "application/octet-stream";
74 | mimeTypes[".caf"] = "audio/x-caf";
75 | mimeTypes[".calx"] = "application/vnd.ms-office.calx";
76 | mimeTypes[".cat"] = "application/vnd.ms-pki.seccat";
77 | mimeTypes[".cc"] = "text/plain";
78 | mimeTypes[".cd"] = "text/plain";
79 | mimeTypes[".cdda"] = "audio/aiff";
80 | mimeTypes[".cdf"] = "application/x-cdf";
81 | mimeTypes[".cer"] = "application/x-x509-ca-cert";
82 | mimeTypes[".cfg"] = "text/plain";
83 | mimeTypes[".chm"] = "application/octet-stream";
84 | mimeTypes[".class"] = "application/x-java-applet";
85 | mimeTypes[".clp"] = "application/x-msclip";
86 | mimeTypes[".cmd"] = "text/plain";
87 | mimeTypes[".cmx"] = "image/x-cmx";
88 | mimeTypes[".cnf"] = "text/plain";
89 | mimeTypes[".cod"] = "image/cis-cod";
90 | mimeTypes[".config"] = "application/xml";
91 | mimeTypes[".contact"] = "text/x-ms-contact";
92 | mimeTypes[".coverage"] = "application/xml";
93 | mimeTypes[".cpio"] = "application/x-cpio";
94 | mimeTypes[".cpp"] = "text/plain";
95 | mimeTypes[".crd"] = "application/x-mscardfile";
96 | mimeTypes[".crl"] = "application/pkix-crl";
97 | mimeTypes[".crt"] = "application/x-x509-ca-cert";
98 | mimeTypes[".cs"] = "text/plain";
99 | mimeTypes[".csdproj"] = "text/plain";
100 | mimeTypes[".csh"] = "application/x-csh";
101 | mimeTypes[".csproj"] = "text/plain";
102 | mimeTypes[".css"] = "text/css";
103 | mimeTypes[".csv"] = "text/csv";
104 | mimeTypes[".cur"] = "application/octet-stream";
105 | mimeTypes[".czx"] = "application/x-czx";
106 | mimeTypes[".cxx"] = "text/plain";
107 | mimeTypes[".dat"] = "application/octet-stream";
108 | mimeTypes[".datasource"] = "application/xml";
109 | mimeTypes[".dbproj"] = "text/plain";
110 | mimeTypes[".dcr"] = "application/x-director";
111 | mimeTypes[".def"] = "text/plain";
112 | mimeTypes[".deploy"] = "application/octet-stream";
113 | mimeTypes[".der"] = "application/x-x509-ca-cert";
114 | mimeTypes[".dgml"] = "application/xml";
115 | mimeTypes[".dib"] = "image/bmp";
116 | mimeTypes[".dif"] = "video/x-dv";
117 | mimeTypes[".dir"] = "application/x-director";
118 | mimeTypes[".disco"] = "text/xml";
119 | mimeTypes[".divx"] = "video/divx";
120 | mimeTypes[".dll"] = "application/x-msdownload";
121 | mimeTypes[".dll.config"] = "text/xml";
122 | mimeTypes[".dlm"] = "text/dlm";
123 | mimeTypes[".doc"] = "application/msword";
124 | mimeTypes[".docm"] = "application/vnd.ms-word.document.macroEnabled.12";
125 | mimeTypes[".docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
126 | mimeTypes[".dot"] = "application/msword";
127 | mimeTypes[".dotm"] = "application/vnd.ms-word.template.macroEnabled.12";
128 | mimeTypes[".dotx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.template";
129 | mimeTypes[".dsp"] = "application/octet-stream";
130 | mimeTypes[".dsw"] = "text/plain";
131 | mimeTypes[".dtd"] = "text/xml";
132 | mimeTypes[".dtsConfig"] = "text/xml";
133 | mimeTypes[".dv"] = "video/x-dv";
134 | mimeTypes[".dvi"] = "application/x-dvi";
135 | mimeTypes[".dwf"] = "drawing/x-dwf";
136 | mimeTypes[".dwg"] = "application/acad";
137 | mimeTypes[".dwp"] = "application/octet-stream";
138 | mimeTypes[".dxf"] = "application/x-dxf";
139 | mimeTypes[".dxr"] = "application/x-director";
140 | mimeTypes[".eml"] = "message/rfc822";
141 | mimeTypes[".emf"] = "image/emf";
142 | mimeTypes[".emz"] = "application/octet-stream";
143 | mimeTypes[".eot"] = "application/vnd.ms-fontobject";
144 | mimeTypes[".eps"] = "application/postscript";
145 | mimeTypes[".es"] = "application/ecmascript";
146 | mimeTypes[".etl"] = "application/etl";
147 | mimeTypes[".etx"] = "text/x-setext";
148 | mimeTypes[".evy"] = "application/envoy";
149 | mimeTypes[".exe"] = "application/x-msdos-program";
150 | mimeTypes[".exe.config"] = "text/xml";
151 | mimeTypes[".f4v"] = "video/mp4";
152 | mimeTypes[".fdf"] = "application/vnd.fdf";
153 | mimeTypes[".fif"] = "application/fractals";
154 | mimeTypes[".filters"] = "application/xml";
155 | mimeTypes[".fla"] = "application/octet-stream";
156 | mimeTypes[".flac"] = "audio/flac";
157 | mimeTypes[".flr"] = "x-world/x-vrml";
158 | mimeTypes[".flv"] = "video/x-flv";
159 | mimeTypes[".fsscript"] = "application/fsharp-script";
160 | mimeTypes[".fsx"] = "application/fsharp-script";
161 | mimeTypes[".generictest"] = "application/xml";
162 | mimeTypes[".geojson"] = "application/geo+json";
163 | mimeTypes[".gif"] = "image/gif";
164 | mimeTypes[".gpx"] = "application/gpx+xml";
165 | mimeTypes[".group"] = "text/x-ms-group";
166 | mimeTypes[".gsm"] = "audio/x-gsm";
167 | mimeTypes[".gtar"] = "application/x-gtar";
168 | mimeTypes[".gz"] = "application/x-gzip";
169 | mimeTypes[".h"] = "text/plain";
170 | mimeTypes[".hdf"] = "application/x-hdf";
171 | mimeTypes[".hdml"] = "text/x-hdml";
172 | mimeTypes[".heic"] = "image/heic";
173 | mimeTypes[".heics"] = "image/heic-sequence";
174 | mimeTypes[".heif"] = "image/heif";
175 | mimeTypes[".heifs"] = "image/heif-sequence";
176 | mimeTypes[".hhc"] = "application/x-oleobject";
177 | mimeTypes[".hhk"] = "application/octet-stream";
178 | mimeTypes[".hhp"] = "application/octet-stream";
179 | mimeTypes[".hlp"] = "application/winhlp";
180 | mimeTypes[".hpp"] = "text/plain";
181 | mimeTypes[".hqx"] = "application/mac-binhex40";
182 | mimeTypes[".hta"] = "application/hta";
183 | mimeTypes[".htc"] = "text/x-component";
184 | mimeTypes[".htm"] = "text/html";
185 | mimeTypes[".html"] = "text/html";
186 | mimeTypes[".htt"] = "text/webviewhtml";
187 | mimeTypes[".hxa"] = "application/xml";
188 | mimeTypes[".hxc"] = "application/xml";
189 | mimeTypes[".hxd"] = "application/octet-stream";
190 | mimeTypes[".hxe"] = "application/xml";
191 | mimeTypes[".hxf"] = "application/xml";
192 | mimeTypes[".hxh"] = "application/octet-stream";
193 | mimeTypes[".hxi"] = "application/octet-stream";
194 | mimeTypes[".hxk"] = "application/xml";
195 | mimeTypes[".hxq"] = "application/octet-stream";
196 | mimeTypes[".hxr"] = "application/octet-stream";
197 | mimeTypes[".hxs"] = "application/octet-stream";
198 | mimeTypes[".hxt"] = "text/html";
199 | mimeTypes[".hxv"] = "application/xml";
200 | mimeTypes[".hxw"] = "application/octet-stream";
201 | mimeTypes[".hxx"] = "text/plain";
202 | mimeTypes[".i"] = "text/plain";
203 | mimeTypes[".ical"] = "text/calendar";
204 | mimeTypes[".icalendar"] = "text/calendar";
205 | mimeTypes[".ico"] = "image/x-icon";
206 | mimeTypes[".ics"] = "text/calendar";
207 | mimeTypes[".idl"] = "text/plain";
208 | mimeTypes[".ief"] = "image/ief";
209 | mimeTypes[".ifb"] = "text/calendar";
210 | mimeTypes[".iii"] = "application/x-iphone";
211 | mimeTypes[".inc"] = "text/plain";
212 | mimeTypes[".inf"] = "application/octet-stream";
213 | mimeTypes[".ini"] = "text/plain";
214 | mimeTypes[".inl"] = "text/plain";
215 | mimeTypes[".ins"] = "application/x-internet-signup";
216 | mimeTypes[".ipa"] = "application/x-itunes-ipa";
217 | mimeTypes[".ipg"] = "application/x-itunes-ipg";
218 | mimeTypes[".ipproj"] = "text/plain";
219 | mimeTypes[".ipsw"] = "application/x-itunes-ipsw";
220 | mimeTypes[".iqy"] = "text/x-ms-iqy";
221 | mimeTypes[".isp"] = "application/x-internet-signup";
222 | mimeTypes[".isma"] = "application/octet-stream";
223 | mimeTypes[".ismv"] = "application/octet-stream";
224 | mimeTypes[".ite"] = "application/x-itunes-ite";
225 | mimeTypes[".itlp"] = "application/x-itunes-itlp";
226 | mimeTypes[".itms"] = "application/x-itunes-itms";
227 | mimeTypes[".itpc"] = "application/x-itunes-itpc";
228 | mimeTypes[".IVF"] = "video/x-ivf";
229 | mimeTypes[".jar"] = "application/java-archive";
230 | mimeTypes[".java"] = "application/octet-stream";
231 | mimeTypes[".jck"] = "application/liquidmotion";
232 | mimeTypes[".jcz"] = "application/liquidmotion";
233 | mimeTypes[".jfif"] = "image/pjpeg";
234 | mimeTypes[".jnlp"] = "application/x-java-jnlp-file";
235 | mimeTypes[".jpb"] = "application/octet-stream";
236 | mimeTypes[".jpe"] = "image/jpeg";
237 | mimeTypes[".jpeg"] = "image/jpeg";
238 | mimeTypes[".jpg"] = "image/jpeg";
239 | mimeTypes[".js"] = "application/javascript";
240 | mimeTypes[".json"] = "application/json";
241 | mimeTypes[".jsx"] = "text/jscript";
242 | mimeTypes[".jsxbin"] = "text/plain";
243 | mimeTypes[".key"] = "application/x-iwork-keynote-sffkey";
244 | mimeTypes[".latex"] = "application/x-latex";
245 | mimeTypes[".library-ms"] = "application/windows-library+xml";
246 | mimeTypes[".lit"] = "application/x-ms-reader";
247 | mimeTypes[".loadtest"] = "application/xml";
248 | mimeTypes[".lpk"] = "application/octet-stream";
249 | mimeTypes[".lsf"] = "video/x-la-asf";
250 | mimeTypes[".lst"] = "text/plain";
251 | mimeTypes[".lsx"] = "video/x-la-asf";
252 | mimeTypes[".lzh"] = "application/octet-stream";
253 | mimeTypes[".m13"] = "application/x-msmediaview";
254 | mimeTypes[".m14"] = "application/x-msmediaview";
255 | mimeTypes[".m1v"] = "video/mpeg";
256 | mimeTypes[".m2t"] = "video/vnd.dlna.mpeg-tts";
257 | mimeTypes[".m2ts"] = "video/vnd.dlna.mpeg-tts";
258 | mimeTypes[".m2v"] = "video/mpeg";
259 | mimeTypes[".m3u"] = "audio/x-mpegurl";
260 | mimeTypes[".m3u8"] = "audio/x-mpegurl";
261 | mimeTypes[".m4a"] = "audio/x-m4a";
262 | mimeTypes[".m4b"] = "audio/m4b";
263 | mimeTypes[".m4p"] = "audio/m4p";
264 | mimeTypes[".m4r"] = "audio/x-m4r";
265 | mimeTypes[".m4v"] = "video/x-m4v";
266 | mimeTypes[".mac"] = "image/x-macpaint";
267 | mimeTypes[".mak"] = "text/plain";
268 | mimeTypes[".man"] = "application/x-troff-man";
269 | mimeTypes[".manifest"] = "application/x-ms-manifest";
270 | mimeTypes[".map"] = "text/plain";
271 | mimeTypes[".master"] = "application/xml";
272 | mimeTypes[".mbox"] = "application/mbox";
273 | mimeTypes[".mda"] = "application/msaccess";
274 | mimeTypes[".mdb"] = "application/x-msaccess";
275 | mimeTypes[".mde"] = "application/msaccess";
276 | mimeTypes[".mdp"] = "application/octet-stream";
277 | mimeTypes[".me"] = "application/x-troff-me";
278 | mimeTypes[".mfp"] = "application/x-shockwave-flash";
279 | mimeTypes[".mht"] = "message/rfc822";
280 | mimeTypes[".mhtml"] = "message/rfc822";
281 | mimeTypes[".mid"] = "audio/mid";
282 | mimeTypes[".midi"] = "audio/mid";
283 | mimeTypes[".mix"] = "application/octet-stream";
284 | mimeTypes[".mk"] = "text/plain";
285 | mimeTypes[".mk3d"] = "video/x-matroska-3d";
286 | mimeTypes[".mka"] = "audio/x-matroska";
287 | mimeTypes[".mkv"] = "video/x-matroska";
288 | mimeTypes[".mmf"] = "application/x-smaf";
289 | mimeTypes[".mno"] = "text/xml";
290 | mimeTypes[".mny"] = "application/x-msmoney";
291 | mimeTypes[".mod"] = "video/mpeg";
292 | mimeTypes[".mov"] = "video/quicktime";
293 | mimeTypes[".movie"] = "video/x-sgi-movie";
294 | mimeTypes[".mp2"] = "video/mpeg";
295 | mimeTypes[".mp2v"] = "video/mpeg";
296 | mimeTypes[".mp3"] = "audio/mpeg";
297 | mimeTypes[".mp4"] = "video/mp4";
298 | mimeTypes[".mp4v"] = "video/mp4";
299 | mimeTypes[".mpa"] = "video/mpeg";
300 | mimeTypes[".mpe"] = "video/mpeg";
301 | mimeTypes[".mpeg"] = "video/mpeg";
302 | mimeTypes[".mpf"] = "application/vnd.ms-mediapackage";
303 | mimeTypes[".mpg"] = "video/mpeg";
304 | mimeTypes[".mpp"] = "application/vnd.ms-project";
305 | mimeTypes[".mpv2"] = "video/mpeg";
306 | mimeTypes[".mqv"] = "video/quicktime";
307 | mimeTypes[".ms"] = "application/x-troff-ms";
308 | mimeTypes[".msg"] = "application/vnd.ms-outlook";
309 | mimeTypes[".msi"] = "application/octet-stream";
310 | mimeTypes[".mso"] = "application/octet-stream";
311 | mimeTypes[".mts"] = "video/vnd.dlna.mpeg-tts";
312 | mimeTypes[".mtx"] = "application/xml";
313 | mimeTypes[".mvb"] = "application/x-msmediaview";
314 | mimeTypes[".mvc"] = "application/x-miva-compiled";
315 | mimeTypes[".mxf"] = "application/mxf";
316 | mimeTypes[".mxp"] = "application/x-mmxp";
317 | mimeTypes[".nc"] = "application/x-netcdf";
318 | mimeTypes[".nsc"] = "video/x-ms-asf";
319 | mimeTypes[".numbers"] = "application/x-iwork-numbers-sffnumbers";
320 | mimeTypes[".nws"] = "message/rfc822";
321 | mimeTypes[".ocx"] = "application/octet-stream";
322 | mimeTypes[".oda"] = "application/oda";
323 | mimeTypes[".odb"] = "application/vnd.oasis.opendocument.database";
324 | mimeTypes[".odc"] = "application/vnd.oasis.opendocument.chart";
325 | mimeTypes[".odf"] = "application/vnd.oasis.opendocument.formula";
326 | mimeTypes[".odg"] = "application/vnd.oasis.opendocument.graphics";
327 | mimeTypes[".odh"] = "text/plain";
328 | mimeTypes[".odi"] = "application/vnd.oasis.opendocument.image";
329 | mimeTypes[".odl"] = "text/plain";
330 | mimeTypes[".odm"] = "application/vnd.oasis.opendocument.text-master";
331 | mimeTypes[".odp"] = "application/vnd.oasis.opendocument.presentation";
332 | mimeTypes[".ods"] = "application/vnd.oasis.opendocument.spreadsheet";
333 | mimeTypes[".odt"] = "application/vnd.oasis.opendocument.text";
334 | mimeTypes[".oga"] = "audio/ogg";
335 | mimeTypes[".ogg"] = "audio/ogg";
336 | mimeTypes[".ogv"] = "video/ogg";
337 | mimeTypes[".ogx"] = "application/ogg";
338 | mimeTypes[".one"] = "application/onenote";
339 | mimeTypes[".onea"] = "application/onenote";
340 | mimeTypes[".onepkg"] = "application/onenote";
341 | mimeTypes[".onetmp"] = "application/onenote";
342 | mimeTypes[".onetoc"] = "application/onenote";
343 | mimeTypes[".onetoc2"] = "application/onenote";
344 | mimeTypes[".opus"] = "audio/ogg; codecs=opus";
345 | mimeTypes[".orderedtest"] = "application/xml";
346 | mimeTypes[".osdx"] = "application/opensearchdescription+xml";
347 | mimeTypes[".otf"] = "application/font-sfnt";
348 | mimeTypes[".otg"] = "application/vnd.oasis.opendocument.graphics-template";
349 | mimeTypes[".oth"] = "application/vnd.oasis.opendocument.text-web";
350 | mimeTypes[".otp"] = "application/vnd.oasis.opendocument.presentation-template";
351 | mimeTypes[".ots"] = "application/vnd.oasis.opendocument.spreadsheet-template";
352 | mimeTypes[".ott"] = "application/vnd.oasis.opendocument.text-template";
353 | mimeTypes[".oxps"] = "application/oxps";
354 | mimeTypes[".oxt"] = "application/vnd.openofficeorg.extension";
355 | mimeTypes[".p10"] = "application/pkcs10";
356 | mimeTypes[".p12"] = "application/x-pkcs12";
357 | mimeTypes[".p7b"] = "application/x-pkcs7-certificates";
358 | mimeTypes[".p7c"] = "application/pkcs7-mime";
359 | mimeTypes[".p7m"] = "application/pkcs7-mime";
360 | mimeTypes[".p7r"] = "application/x-pkcs7-certreqresp";
361 | mimeTypes[".p7s"] = "application/pkcs7-signature";
362 | mimeTypes[".pages"] = "application/x-iwork-pages-sffpages";
363 | mimeTypes[".pbm"] = "image/x-portable-bitmap";
364 | mimeTypes[".pcast"] = "application/x-podcast";
365 | mimeTypes[".pct"] = "image/pict";
366 | mimeTypes[".pcx"] = "application/octet-stream";
367 | mimeTypes[".pcz"] = "application/octet-stream";
368 | mimeTypes[".pdf"] = "application/pdf";
369 | mimeTypes[".pfb"] = "application/octet-stream";
370 | mimeTypes[".pfm"] = "application/octet-stream";
371 | mimeTypes[".pfx"] = "application/x-pkcs12";
372 | mimeTypes[".pgm"] = "image/x-portable-graymap";
373 | mimeTypes[".pic"] = "image/pict";
374 | mimeTypes[".pict"] = "image/pict";
375 | mimeTypes[".pkgdef"] = "text/plain";
376 | mimeTypes[".pkgundef"] = "text/plain";
377 | mimeTypes[".pko"] = "application/vnd.ms-pki.pko";
378 | mimeTypes[".pls"] = "audio/scpls";
379 | mimeTypes[".pma"] = "application/x-perfmon";
380 | mimeTypes[".pmc"] = "application/x-perfmon";
381 | mimeTypes[".pml"] = "application/x-perfmon";
382 | mimeTypes[".pmr"] = "application/x-perfmon";
383 | mimeTypes[".pmw"] = "application/x-perfmon";
384 | mimeTypes[".png"] = "image/x-png";
385 | mimeTypes[".pnm"] = "image/x-portable-anymap";
386 | mimeTypes[".pnt"] = "image/x-macpaint";
387 | mimeTypes[".pntg"] = "image/x-macpaint";
388 | mimeTypes[".pnz"] = "image/png";
389 | mimeTypes[".pot"] = "application/vnd.ms-powerpoint";
390 | mimeTypes[".potm"] = "application/vnd.ms-powerpoint.template.macroEnabled.12";
391 | mimeTypes[".potx"] = "application/vnd.openxmlformats-officedocument.presentationml.template";
392 | mimeTypes[".ppa"] = "application/vnd.ms-powerpoint";
393 | mimeTypes[".ppam"] = "application/vnd.ms-powerpoint.addin.macroEnabled.12";
394 | mimeTypes[".ppm"] = "image/x-portable-pixmap";
395 | mimeTypes[".pps"] = "application/vnd.ms-powerpoint";
396 | mimeTypes[".ppsm"] = "application/vnd.ms-powerpoint.slideshow.macroEnabled.12";
397 | mimeTypes[".ppsx"] = "application/vnd.openxmlformats-officedocument.presentationml.slideshow";
398 | mimeTypes[".ppt"] = "application/vnd.ms-powerpoint";
399 | mimeTypes[".pptm"] = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
400 | mimeTypes[".pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
401 | mimeTypes[".prf"] = "application/pics-rules";
402 | mimeTypes[".prm"] = "application/octet-stream";
403 | mimeTypes[".prx"] = "application/octet-stream";
404 | mimeTypes[".ps"] = "application/postscript";
405 | mimeTypes[".psc1"] = "application/PowerShell";
406 | mimeTypes[".psd"] = "application/octet-stream";
407 | mimeTypes[".psess"] = "application/xml";
408 | mimeTypes[".psm"] = "application/octet-stream";
409 | mimeTypes[".psp"] = "application/octet-stream";
410 | mimeTypes[".pst"] = "application/vnd.ms-outlook";
411 | mimeTypes[".pub"] = "application/x-mspublisher";
412 | mimeTypes[".pwz"] = "application/vnd.ms-powerpoint";
413 | mimeTypes[".qht"] = "text/x-html-insertion";
414 | mimeTypes[".qhtm"] = "text/x-html-insertion";
415 | mimeTypes[".qt"] = "video/quicktime";
416 | mimeTypes[".qti"] = "image/x-quicktime";
417 | mimeTypes[".qtif"] = "image/x-quicktime";
418 | mimeTypes[".qtl"] = "application/x-quicktimeplayer";
419 | mimeTypes[".qxd"] = "application/octet-stream";
420 | mimeTypes[".ra"] = "audio/x-pn-realaudio";
421 | mimeTypes[".ram"] = "audio/x-pn-realaudio";
422 | mimeTypes[".rar"] = "application/x-rar-compressed";
423 | mimeTypes[".ras"] = "image/x-cmu-raster";
424 | mimeTypes[".rat"] = "application/rat-file";
425 | mimeTypes[".rc"] = "text/plain";
426 | mimeTypes[".rc2"] = "text/plain";
427 | mimeTypes[".rct"] = "text/plain";
428 | mimeTypes[".rdlc"] = "application/xml";
429 | mimeTypes[".reg"] = "text/plain";
430 | mimeTypes[".resx"] = "application/xml";
431 | mimeTypes[".rf"] = "image/vnd.rn-realflash";
432 | mimeTypes[".rgb"] = "image/x-rgb";
433 | mimeTypes[".rgs"] = "text/plain";
434 | mimeTypes[".rm"] = "application/vnd.rn-realmedia";
435 | mimeTypes[".rmi"] = "audio/mid";
436 | mimeTypes[".rmp"] = "application/vnd.rn-rn_music_package";
437 | mimeTypes[".rmvb"] = "application/vnd.rn-realmedia-vbr";
438 | mimeTypes[".roff"] = "application/x-troff";
439 | mimeTypes[".rpm"] = "audio/x-pn-realaudio-plugin";
440 | mimeTypes[".rqy"] = "text/x-ms-rqy";
441 | mimeTypes[".rtf"] = "application/rtf";
442 | mimeTypes[".rtx"] = "text/richtext";
443 | mimeTypes[".rvt"] = "application/octet-stream";
444 | mimeTypes[".ruleset"] = "application/xml";
445 | mimeTypes[".s"] = "text/plain";
446 | mimeTypes[".safariextz"] = "application/x-safari-safariextz";
447 | mimeTypes[".scd"] = "application/x-msschedule";
448 | mimeTypes[".scr"] = "text/plain";
449 | mimeTypes[".sct"] = "text/scriptlet";
450 | mimeTypes[".sd2"] = "audio/x-sd2";
451 | mimeTypes[".sdp"] = "application/sdp";
452 | mimeTypes[".sea"] = "application/octet-stream";
453 | mimeTypes[".searchConnector-ms"] = "application/windows-search-connector+xml";
454 | mimeTypes[".setpay"] = "application/set-payment-initiation";
455 | mimeTypes[".setreg"] = "application/set-registration-initiation";
456 | mimeTypes[".settings"] = "application/xml";
457 | mimeTypes[".sgimb"] = "application/x-sgimb";
458 | mimeTypes[".sgml"] = "text/sgml";
459 | mimeTypes[".sh"] = "application/x-sh";
460 | mimeTypes[".shar"] = "application/x-shar";
461 | mimeTypes[".shtml"] = "text/html";
462 | mimeTypes[".sit"] = "application/x-stuffit";
463 | mimeTypes[".sitemap"] = "application/xml";
464 | mimeTypes[".skin"] = "application/xml";
465 | mimeTypes[".skp"] = "application/x-koan";
466 | mimeTypes[".sldm"] = "application/vnd.ms-powerpoint.slide.macroEnabled.12";
467 | mimeTypes[".sldx"] = "application/vnd.openxmlformats-officedocument.presentationml.slide";
468 | mimeTypes[".slk"] = "application/vnd.ms-excel";
469 | mimeTypes[".sln"] = "text/plain";
470 | mimeTypes[".slupkg-ms"] = "application/x-ms-license";
471 | mimeTypes[".smd"] = "audio/x-smd";
472 | mimeTypes[".smi"] = "application/octet-stream";
473 | mimeTypes[".smx"] = "audio/x-smd";
474 | mimeTypes[".smz"] = "audio/x-smd";
475 | mimeTypes[".snd"] = "audio/basic";
476 | mimeTypes[".snippet"] = "application/xml";
477 | mimeTypes[".snp"] = "application/octet-stream";
478 | mimeTypes[".sql"] = "application/sql";
479 | mimeTypes[".sol"] = "text/plain";
480 | mimeTypes[".sor"] = "text/plain";
481 | mimeTypes[".spc"] = "application/x-pkcs7-certificates";
482 | mimeTypes[".spl"] = "application/futuresplash";
483 | mimeTypes[".spx"] = "audio/ogg";
484 | mimeTypes[".src"] = "application/x-wais-source";
485 | mimeTypes[".srf"] = "text/plain";
486 | mimeTypes[".SSISDeploymentManifest"] = "text/xml";
487 | mimeTypes[".ssm"] = "application/streamingmedia";
488 | mimeTypes[".sst"] = "application/vnd.ms-pki.certstore";
489 | mimeTypes[".stl"] = "application/vnd.ms-pki.stl";
490 | mimeTypes[".sv4cpio"] = "application/x-sv4cpio";
491 | mimeTypes[".sv4crc"] = "application/x-sv4crc";
492 | mimeTypes[".svc"] = "application/xml";
493 | mimeTypes[".svg"] = "image/svg+xml";
494 | mimeTypes[".swf"] = "application/x-shockwave-flash";
495 | mimeTypes[".step"] = "application/step";
496 | mimeTypes[".stp"] = "application/step";
497 | mimeTypes[".t"] = "application/x-troff";
498 | mimeTypes[".tar"] = "application/x-tar";
499 | mimeTypes[".tcl"] = "application/x-tcl";
500 | mimeTypes[".testrunconfig"] = "application/xml";
501 | mimeTypes[".testsettings"] = "application/xml";
502 | mimeTypes[".tex"] = "application/x-tex";
503 | mimeTypes[".texi"] = "application/x-texinfo";
504 | mimeTypes[".texinfo"] = "application/x-texinfo";
505 | mimeTypes[".tgz"] = "application/x-compressed";
506 | mimeTypes[".thmx"] = "application/vnd.ms-officetheme";
507 | mimeTypes[".thn"] = "application/octet-stream";
508 | mimeTypes[".tif"] = "image/tiff";
509 | mimeTypes[".tiff"] = "image/tiff";
510 | mimeTypes[".tlh"] = "text/plain";
511 | mimeTypes[".tli"] = "text/plain";
512 | mimeTypes[".toc"] = "application/octet-stream";
513 | mimeTypes[".tr"] = "application/x-troff";
514 | mimeTypes[".trm"] = "application/x-msterminal";
515 | mimeTypes[".trx"] = "application/xml";
516 | mimeTypes[".ts"] = "video/vnd.dlna.mpeg-tts";
517 | mimeTypes[".tsv"] = "text/tab-separated-values";
518 | mimeTypes[".ttf"] = "application/font-sfnt";
519 | mimeTypes[".tts"] = "video/vnd.dlna.mpeg-tts";
520 | mimeTypes[".txt"] = "text/plain";
521 | mimeTypes[".u32"] = "application/octet-stream";
522 | mimeTypes[".uls"] = "text/iuls";
523 | mimeTypes[".user"] = "text/plain";
524 | mimeTypes[".ustar"] = "application/x-ustar";
525 | mimeTypes[".vb"] = "text/plain";
526 | mimeTypes[".vbdproj"] = "text/plain";
527 | mimeTypes[".vbk"] = "video/mpeg";
528 | mimeTypes[".vbproj"] = "text/plain";
529 | mimeTypes[".vbs"] = "text/vbscript";
530 | mimeTypes[".vcf"] = "text/x-vcard";
531 | mimeTypes[".vcproj"] = "application/xml";
532 | mimeTypes[".vcs"] = "text/plain";
533 | mimeTypes[".vcxproj"] = "application/xml";
534 | mimeTypes[".vddproj"] = "text/plain";
535 | mimeTypes[".vdp"] = "text/plain";
536 | mimeTypes[".vdproj"] = "text/plain";
537 | mimeTypes[".vdx"] = "application/vnd.ms-visio.viewer";
538 | mimeTypes[".vml"] = "text/xml";
539 | mimeTypes[".vscontent"] = "application/xml";
540 | mimeTypes[".vsct"] = "text/xml";
541 | mimeTypes[".vsd"] = "application/vnd.visio";
542 | mimeTypes[".vsi"] = "application/ms-vsi";
543 | mimeTypes[".vsix"] = "application/vsix";
544 | mimeTypes[".vsixlangpack"] = "text/xml";
545 | mimeTypes[".vsixmanifest"] = "text/xml";
546 | mimeTypes[".vsmdi"] = "application/xml";
547 | mimeTypes[".vspscc"] = "text/plain";
548 | mimeTypes[".vss"] = "application/vnd.visio";
549 | mimeTypes[".vsscc"] = "text/plain";
550 | mimeTypes[".vssettings"] = "text/xml";
551 | mimeTypes[".vssscc"] = "text/plain";
552 | mimeTypes[".vst"] = "application/vnd.visio";
553 | mimeTypes[".vstemplate"] = "text/xml";
554 | mimeTypes[".vsto"] = "application/x-ms-vsto";
555 | mimeTypes[".vsw"] = "application/vnd.visio";
556 | mimeTypes[".vsx"] = "application/vnd.visio";
557 | mimeTypes[".vtt"] = "text/vtt";
558 | mimeTypes[".vtx"] = "application/vnd.visio";
559 | mimeTypes[".wasm"] = "application/wasm";
560 | mimeTypes[".wav"] = "audio/wav";
561 | mimeTypes[".wave"] = "audio/wav";
562 | mimeTypes[".wax"] = "audio/x-ms-wax";
563 | mimeTypes[".wbk"] = "application/msword";
564 | mimeTypes[".wbmp"] = "image/vnd.wap.wbmp";
565 | mimeTypes[".wcm"] = "application/vnd.ms-works";
566 | mimeTypes[".wdb"] = "application/vnd.ms-works";
567 | mimeTypes[".wdp"] = "image/vnd.ms-photo";
568 | mimeTypes[".webarchive"] = "application/x-safari-webarchive";
569 | mimeTypes[".webm"] = "video/webm";
570 | mimeTypes[".webp"] = "image/webp";
571 | mimeTypes[".webtest"] = "application/xml";
572 | mimeTypes[".wiq"] = "application/xml";
573 | mimeTypes[".wiz"] = "application/msword";
574 | mimeTypes[".wks"] = "application/vnd.ms-works";
575 | mimeTypes[".WLMP"] = "application/wlmoviemaker";
576 | mimeTypes[".wlpginstall"] = "application/x-wlpg-detect";
577 | mimeTypes[".wlpginstall3"] = "application/x-wlpg3-detect";
578 | mimeTypes[".wm"] = "video/x-ms-wm";
579 | mimeTypes[".wma"] = "audio/x-ms-wma";
580 | mimeTypes[".wmd"] = "application/x-ms-wmd";
581 | mimeTypes[".wmf"] = "application/x-msmetafile";
582 | mimeTypes[".wml"] = "text/vnd.wap.wml";
583 | mimeTypes[".wmlc"] = "application/vnd.wap.wmlc";
584 | mimeTypes[".wmls"] = "text/vnd.wap.wmlscript";
585 | mimeTypes[".wmlsc"] = "application/vnd.wap.wmlscriptc";
586 | mimeTypes[".wmp"] = "video/x-ms-wmp";
587 | mimeTypes[".wmv"] = "video/x-ms-wmv";
588 | mimeTypes[".wmx"] = "video/x-ms-wmx";
589 | mimeTypes[".wmz"] = "application/x-ms-wmz";
590 | mimeTypes[".woff"] = "application/font-woff";
591 | mimeTypes[".woff2"] = "application/font-woff2";
592 | mimeTypes[".wpl"] = "application/vnd.ms-wpl";
593 | mimeTypes[".wps"] = "application/vnd.ms-works";
594 | mimeTypes[".wri"] = "application/x-mswrite";
595 | mimeTypes[".wrl"] = "x-world/x-vrml";
596 | mimeTypes[".wrz"] = "x-world/x-vrml";
597 | mimeTypes[".wsc"] = "text/scriptlet";
598 | mimeTypes[".wsdl"] = "text/xml";
599 | mimeTypes[".wvx"] = "video/x-ms-wvx";
600 | mimeTypes[".x"] = "application/directx";
601 | mimeTypes[".xaf"] = "x-world/x-vrml";
602 | mimeTypes[".xaml"] = "application/xaml+xml";
603 | mimeTypes[".xap"] = "application/x-silverlight-app";
604 | mimeTypes[".xbap"] = "application/x-ms-xbap";
605 | mimeTypes[".xbm"] = "image/x-xbitmap";
606 | mimeTypes[".xdr"] = "text/plain";
607 | mimeTypes[".xht"] = "application/xhtml+xml";
608 | mimeTypes[".xhtml"] = "application/xhtml+xml";
609 | mimeTypes[".xla"] = "application/vnd.ms-excel";
610 | mimeTypes[".xlam"] = "application/vnd.ms-excel.addin.macroEnabled.12";
611 | mimeTypes[".xlc"] = "application/vnd.ms-excel";
612 | mimeTypes[".xld"] = "application/vnd.ms-excel";
613 | mimeTypes[".xlk"] = "application/vnd.ms-excel";
614 | mimeTypes[".xll"] = "application/vnd.ms-excel";
615 | mimeTypes[".xlm"] = "application/vnd.ms-excel";
616 | mimeTypes[".xls"] = "application/vnd.ms-excel";
617 | mimeTypes[".xlsb"] = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
618 | mimeTypes[".xlsm"] = "application/vnd.ms-excel.sheet.macroEnabled.12";
619 | mimeTypes[".xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
620 | mimeTypes[".xlt"] = "application/vnd.ms-excel";
621 | mimeTypes[".xltm"] = "application/vnd.ms-excel.template.macroEnabled.12";
622 | mimeTypes[".xltx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.template";
623 | mimeTypes[".xlw"] = "application/vnd.ms-excel";
624 | mimeTypes[".xml"] = "text/xml";
625 | mimeTypes[".xmp"] = "application/octet-stream";
626 | mimeTypes[".xmta"] = "application/xml";
627 | mimeTypes[".xof"] = "x-world/x-vrml";
628 | mimeTypes[".XOML"] = "text/plain";
629 | mimeTypes[".xpm"] = "image/x-xpixmap";
630 | mimeTypes[".xps"] = "application/vnd.ms-xpsdocument";
631 | mimeTypes[".xrm-ms"] = "text/xml";
632 | mimeTypes[".xsc"] = "application/xml";
633 | mimeTypes[".xsd"] = "text/xml";
634 | mimeTypes[".xsf"] = "text/xml";
635 | mimeTypes[".xsl"] = "text/xml";
636 | mimeTypes[".xslt"] = "text/xml";
637 | mimeTypes[".xsn"] = "application/octet-stream";
638 | mimeTypes[".xss"] = "application/xml";
639 | mimeTypes[".xspf"] = "application/xspf+xml";
640 | mimeTypes[".xtp"] = "application/octet-stream";
641 | mimeTypes[".xwd"] = "image/x-xwindowdump";
642 | mimeTypes[".z"] = "application/x-compress";
643 | mimeTypes[".zip"] = "application/x-zip-compressed";
644 |
645 | return mimeTypes;
646 | }
647 |
648 | std::string getFormattedTime(void)
649 | {
650 | time_t rawtime;
651 | struct tm *timeinfo;
652 |
653 | time(&rawtime);
654 | timeinfo = gmtime(&rawtime);
655 |
656 | char formattedTime[30];
657 | strftime(formattedTime, sizeof(formattedTime), "%a, %d %b %Y %H:%M:%S GMT", timeinfo);
658 |
659 | return std::string(formattedTime);
660 | }
661 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | # include "Server.hpp"
2 | # include "Config.hpp"
3 |
4 | int main(int ac, char **av) {
5 | if (ac > 2) {
6 | std::cerr << "Error: wrong number of arguments" << std::endl;
7 | return ERROR;
8 | }
9 |
10 | std::string defFile;
11 | if (ac == 2)
12 | defFile = av[1];
13 | else
14 | defFile = "configs/webserv.conf";
15 |
16 | std::vector servers;
17 | char *file = const_cast(defFile.c_str());
18 | Config config(file);
19 | parsefile(config);
20 |
21 | if (config._servers.size() == 0) {
22 | std::cerr << "Error: no servers found" << std::endl;
23 | return ERROR;
24 | }
25 |
26 | // Iterator over servers
27 | for (std::map >::iterator it = config._serversByPort.begin(); it != config._serversByPort.end(); it++) {
28 | servers.push_back(Server(it->first, it->second));
29 | }
30 |
31 | for (size_t i = 0; i < servers.size(); i++) {
32 | servers[i].start();
33 | }
34 |
35 | fd_set masterSet, readSet, writeSet;
36 | FD_ZERO(&masterSet);
37 | FD_ZERO(&readSet);
38 | FD_ZERO(&writeSet);
39 |
40 | for (size_t i = 0; i < servers.size(); i++) {
41 | FD_SET(servers[i].getSocket(), &masterSet);
42 | }
43 |
44 | while (true) {
45 | readSet = masterSet;
46 | writeSet = masterSet;
47 | int selectRes = select(FD_SETSIZE, &readSet, &writeSet, NULL, NULL);
48 |
49 | if (selectRes <= 0) {
50 | continue;
51 | }
52 |
53 | for (size_t i = 0; i < servers.size(); i++) {
54 | servers[i].handleClients(readSet, writeSet, masterSet);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/request/Request.cpp:
--------------------------------------------------------------------------------
1 | #include "Request.hpp"
2 | #include
3 |
4 | // wait for the body to be read
5 | int Request::waitForBody(size_t headerlength)
6 | {
7 | size_t bodyLengthPos = _REQ.str().find("Content-Length");
8 | size_t chunkedPos = _REQ.str().find("Transfer-Encoding:");
9 | if (bodyLengthPos != std::string::npos) // content length request
10 | {
11 | // check if the body is read
12 | size_t bodyLength = getBodyLength(_REQ.str().substr(bodyLengthPos + 16, \
13 | _REQ.str().find("\r\n", bodyLengthPos + 16) - bodyLengthPos - 16));
14 | std::string body = _REQ.str().substr(headerlength + 4);
15 | if (bodyLength == 0 || _error == 400 || body.size() >= bodyLength)
16 | {
17 | _isBodyRead = true;
18 | _requestLength = _REQ.str().size();
19 | _request = _REQ.str();
20 | return DONE;
21 | }
22 | }
23 | else if (chunkedPos != std::string::npos \
24 | && _REQ.str().substr(chunkedPos, _REQ.str().find("\r\n", chunkedPos) \
25 | - chunkedPos).find("chunked") != std::string::npos) // chunked request
26 | {
27 | // check if the body is read
28 | if (_REQ.str().find("\r\n0\r\n\r\n", chunkedPos) != std::string::npos)
29 | {
30 | _isBodyRead = true;
31 | _requestLength = _REQ.str().size();
32 | _request = _REQ.str();
33 | return DONE;
34 | }
35 | }
36 | else // no body
37 | {
38 | _isBodyRead = true;
39 | _requestLength = _REQ.str().size();
40 | _request = _REQ.str();
41 | return DONE;
42 | }
43 | return (0);
44 | }
45 |
46 | // Receive the request from the client
47 | int Request::recvRequest() {
48 | char buffer[1000000] = {0};
49 | size_t headerlength = 0;
50 | int readRes = recv(_clientSocket, buffer, sizeof(buffer) - 1, 0);
51 | // std::cout << "readRes: " << readRes << std::endl;
52 | if (readRes == -1) {
53 | return DONE;
54 | }
55 | // if client disconnected
56 | else if (readRes == 0) {
57 | return DISCONNECTED;
58 | }
59 | buffer[readRes] = '\0';
60 | // std::cout << "size: " << strlen (buffer) << std::endl;
61 | this->_REQ.write(buffer, readRes);
62 | headerlength = _REQ.str().find("\r\n\r\n");
63 | if (headerlength != std::string::npos && !_isBodyRead) {
64 | _isHeadersRead = true;
65 | return waitForBody(headerlength);
66 |
67 | }
68 | return (0);
69 | }
70 | // Handle the request received on the provided client socket
71 | int Request::handleRequest(int port) {
72 | _port = port;
73 | // Receive the request from the client
74 | int rcvRes = recvRequest();
75 | if (rcvRes == DISCONNECTED) {
76 | return DISCONNECTED;
77 | }
78 | // if the request is received and the body is read
79 | if (rcvRes == DONE && _isBodyRead) {
80 | parsseRequest(); // parse the request received from the client and fill the request object
81 | std::cout << "- - " << this->_method << " " << this->_URI << " " << this->_httpVersion << std::endl;
82 | }
83 | return (0);
84 | }
85 |
86 | Request::Request(int clientSocket, std::vector servers) :
87 | _REQ(),
88 | _request(""),
89 | _requestLength(0),
90 | _httpVersion(""),
91 | _body(""),
92 | _URI(""),
93 | _method(""),
94 | _queries(),
95 | _boundary(""),
96 | _boundaryBody(NULL),
97 | _headers(),
98 | _cookies(""),
99 | _config(),
100 | _servers(servers),
101 | _bodySize(0),
102 | _error(0),
103 | _keepAlive(1),
104 | _isHeadersRead(false),
105 | _clientSocket(clientSocket),
106 | _isBodyRead(false),
107 | _checkBoundary(false)
108 | {
109 | }
110 |
111 | Request::Request() :
112 | _REQ(),
113 | _request(""),
114 | _requestLength(0),
115 | _httpVersion(""),
116 | _body(""),
117 | _URI(""),
118 | _method(""),
119 | _queries(),
120 | _boundary(""),
121 | _boundaryBody(NULL),
122 | _headers(),
123 | _cookies(""),
124 | _config(),
125 | _servers(),
126 | _bodySize(0),
127 | _error(0),
128 | _keepAlive(1),
129 | _isHeadersRead(false),
130 | _clientSocket(-1),
131 | _isBodyRead(false),
132 | _checkBoundary(false)
133 | {
134 | }
135 |
136 | void Request::parsseRequest()
137 | {
138 | size_t nextPos = 0;
139 | parsseMethod(nextPos);
140 | if (_error!= 501)
141 | {
142 | parssePath_Queries(nextPos); // parse the path and the queries of the request
143 | if (_error != 403)
144 | {
145 | parsseHTTPversion(nextPos); // parse the HTTP version of the request
146 | parsseHeaders(nextPos); // parse the headers of the request and fill the headers map
147 | parsseBody(nextPos); // parse the body of the request and fill the body string
148 | HandelDeleteMethod(); // handle the delete method
149 | // std::cout << _request << std::endl;
150 | // parse the method of the request (GET, POST, DELETE)
151 | }
152 | }
153 |
154 | }
155 |
156 | void Request::parsseMethod(size_t &methodPos)
157 | {
158 | // loop until the method is read or the end of the request is reached
159 | for (; methodPos < _requestLength && _request[methodPos] != ' '; methodPos++)
160 | _method += _request[methodPos];
161 | if (_method != "GET" && _method != "POST" && _method != "DELETE")
162 | _error = 501; // method not implemented (not GET, POST or DELETE)
163 | }
164 |
165 | void Request::parssePath_Queries(size_t &URI_Pos)
166 | {
167 |
168 | std::string URI = "";
169 | size_t queryPos;
170 | // skip the spaces
171 | for (; URI_Pos < _requestLength && _request[URI_Pos] == ' '; URI_Pos++);
172 | // loop until the path is read or the end of the request is reached
173 | for (; URI_Pos < _requestLength && _request[URI_Pos] != ' '; URI_Pos++)
174 | URI += _request[URI_Pos];
175 | queryPos = URI.find('?'); // check if the path contains queries
176 | if (queryPos != std::string::npos)
177 | {
178 | _URI = URI.substr(0, queryPos);
179 | _queries = URI.substr(queryPos + 1);
180 | }
181 | else {
182 | _URI = URI;
183 | }
184 | if (URI.find("..") != std::string::npos)
185 | _error = 403;
186 | }
187 |
188 | void Request::parsseHTTPversion(size_t &_httpversion_Pos)
189 | {
190 | // skip the spaces
191 | for (; _httpversion_Pos < _requestLength && _request[_httpversion_Pos] == ' '; _httpversion_Pos++);
192 | // loop until the HTTP version is read or the end of the request is reached
193 | for (; _httpversion_Pos < _requestLength && _request[_httpversion_Pos] != ' ' \
194 | && _request[_httpversion_Pos] != '\r'; _httpversion_Pos++)
195 | _httpVersion += _request[_httpversion_Pos];
196 | }
197 |
198 | void Request::parsseHeaders(size_t &_hpos)
199 | {
200 | std::string _key;
201 | size_t _headerkeyPos;
202 | size_t _headerValuePos;
203 | _keepAlive = false; // set the keep alive to false by default (if the connection header is not found)
204 | size_t bodypos = _request.find("\r\n\r\n", _hpos); // find the end of the headers
205 | if (bodypos == std::string::npos)
206 | return;
207 | // loop until the end of the headers is reached
208 | while (_hpos < bodypos)
209 | {
210 | _headerkeyPos = _request.find(':', _hpos);
211 | if (_headerkeyPos == std::string::npos)
212 | break;
213 | _key = _request.substr(_hpos + 2, _headerkeyPos - _hpos - 2);
214 | if ((_headerValuePos = _request.find("\r\n", _headerkeyPos)) == std::string::npos)
215 | return;
216 | _headers[_key] = _request.substr(_headerkeyPos + 2, _headerValuePos - _headerkeyPos - 2);
217 | if (_key == "Connection" && _headers[_key] == "keep-alive")
218 | _keepAlive = true;
219 | _hpos = _headerValuePos; // increment the position of the headers
220 |
221 | // use this comment to print the headers of the request (for debugging)
222 | // std::cout << '$' << _key << "$ : " << '$' << this->_headers[_key] << "$" << std::endl;
223 | }
224 | if (_headers.find("Cookie") != _headers.end()) {
225 | _cookies = _headers["Cookie"];
226 | _headers.erase("Cookie");
227 | }
228 | else if(_headers.find("Cookies") != _headers.end()) {
229 | _cookies = _headers["Cookies"];
230 | _headers.erase("Cookies");
231 | }
232 | else
233 | _cookies = "";
234 |
235 | }
236 |
237 | // count the number of boundaries in the request
238 | size_t Request::countboundaries(size_t pos)
239 | {
240 | size_t count = 0;
241 | while ((pos = _request.find(_boundary, pos)) != std::string::npos)
242 | {
243 | count++;
244 | pos += _boundary.length();
245 | }
246 | return count;
247 | }
248 | // get the headers of the boundary body
249 | std::map getboundaryHeaders(std::string headers)
250 | {
251 | std::map _headers;
252 | std::string _key;
253 | size_t _headerkeyPos;
254 | size_t _headerValuePos;
255 | size_t _hpos = 0;
256 | size_t headersLength = headers.size();
257 | // loop until the end of the headers is reached
258 | while (_hpos < headersLength)
259 | {
260 | _headerkeyPos = headers.find(':', _hpos);
261 | if (_headerkeyPos == std::string::npos)
262 | break;
263 | _key = headers.substr(_hpos, _headerkeyPos - _hpos);
264 | if ((_headerValuePos = headers.find("\r\n", _headerkeyPos)) == std::string::npos)
265 | return _headers;
266 | _headers[_key] = headers.substr(_headerkeyPos + 2, _headerValuePos - _headerkeyPos - 2);
267 | _hpos = _headerValuePos + 2;
268 | }
269 | return _headers;
270 | }
271 | void Request::getChunkedBody(size_t &_bodyPos)
272 | {
273 | std::string tmp = _request.substr(_bodyPos + 4);
274 | size_t _bodySize = tmp.size();
275 | for(size_t i = 0; i < _bodySize; i++)
276 | {
277 | std::string chunkedSize = "";
278 | size_t j = i;
279 | for (; tmp[j] != '\r'; j++)
280 | chunkedSize += tmp[j];
281 | i = j + 2;
282 | int chunkIntValue = hexStringToInt(chunkedSize); // convert the chunk size from hex to int
283 | if (chunkIntValue == 0)
284 | break;
285 | _body += tmp.substr(i, chunkIntValue);
286 | i += chunkIntValue + 1;
287 | }
288 | }
289 | // find the config of the request if there is multiple servers
290 | void Request::findConfig()
291 | {
292 | // check if the host is found in the headers map
293 | if (_headers.find("Host") != _headers.end())
294 | {
295 | std::string host = _headers["Host"];
296 | for (std::vector::iterator it = _servers.begin(); it != _servers.end(); it++)
297 | {
298 | // check if the host is found in the servers vector
299 | if (it->getString(SERVER_NAME) == host || it->getString(SERVER_NAME) + ":" + std::to_string(_port) == host)
300 | {
301 | _config = *it;
302 | return;
303 | }
304 | }
305 | }
306 | _config = _servers[0];
307 | }
308 |
309 |
310 | // create a file with the provided name and body
311 | void Request::creatFile(std::string fileName, std::string body)
312 | {
313 | std::ofstream file(fileName.c_str());
314 | if (!file) {
315 | _error = 500;
316 | }
317 | else
318 | {
319 | file << body;
320 | file.close();
321 | _error = 201;
322 | }
323 | }
324 |
325 |
326 | // create the file if the body is a file and the body size is less than the max body size
327 | void Request::creatUploadFile(BoundaryBody *headBoundaryBody)
328 | {
329 |
330 | size_t filenamePos = headBoundaryBody->headers["Content-Disposition"].rfind("; filename=");
331 | if (filenamePos == std::string::npos)
332 | {
333 | headBoundaryBody->filename = "";
334 | headBoundaryBody->_isFile = false;
335 | }
336 | else
337 | {
338 | headBoundaryBody->filename = headBoundaryBody->headers["Content-Disposition"].substr(filenamePos + 12, \
339 | headBoundaryBody->headers["Content-Disposition"].length() - filenamePos - 13);
340 | headBoundaryBody->_isFile = true;
341 | std::string uplaodPath = getUploadPath();
342 | creatFile(uplaodPath + headBoundaryBody->filename, headBoundaryBody->_body);
343 | }
344 |
345 | }
346 |
347 | void Request::getBoundaries(size_t &_bodyPos)
348 | {
349 | _boundary = "--" + _headers["Content-Type"].substr(_headers["Content-Type"].find("boundary") + 9);
350 | size_t bodyCount = countboundaries(_bodyPos);
351 | _bodyPos += 4 + _boundary.length() + 2;
352 | BoundaryBody *headBoundaryBody = new BoundaryBody;
353 | _boundaryBody = headBoundaryBody;
354 | while (bodyCount > 1 && headBoundaryBody != NULL)
355 | {
356 | std::string tmpbody = _request.substr(_bodyPos, _request.find(_boundary, _bodyPos) - _bodyPos);
357 | headBoundaryBody->_body = tmpbody.substr(tmpbody.find("\r\n\r\n") + 4);
358 | headBoundaryBody->headers = getboundaryHeaders(tmpbody.substr(0, tmpbody.find("\r\n\r\n") + 4));
359 | if (bodyCount > 2)
360 | headBoundaryBody->next = new BoundaryBody;
361 | else
362 | headBoundaryBody->next = NULL;
363 | if (_method == "POST")
364 | {
365 | if (isMethodAllowed())
366 | creatUploadFile(headBoundaryBody); // create the file if the body is a file
367 | else
368 | _error = 405;
369 | }
370 | bodyCount--;
371 | headBoundaryBody = headBoundaryBody->next;
372 | _bodyPos += tmpbody.length() + _boundary.length() + 2;
373 | }
374 | }
375 |
376 | // generate a random string to name the uploaded file
377 | std::string Request::generateRandomString()
378 | {
379 | const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
380 |
381 | std::string randomFileName= "";
382 |
383 | // Seed the random number generator with the current time
384 | std::srand(static_cast(std::time(nullptr)));
385 |
386 | for (int i = 0; i < 10; i++) {
387 | // Generate a random index within the range of valid characters
388 | int randomIndex = std::rand() % characters.length();
389 | // Append the selected character to the randomFileName
390 | randomFileName += characters[randomIndex];
391 | }
392 |
393 | return randomFileName;
394 |
395 | }
396 |
397 | void Request::parsseBody(size_t &_bodyPos)
398 | {
399 | findConfig();
400 | if (_headers.find("Content-Length") != _headers.end() && _headers.find("Transfer-Encoding") == _headers.end() \
401 | && _headers["Transfer-Encoding"].find("chunked") == std::string::npos)
402 | {
403 | std::istringstream iss(_headers["Content-Length"]);
404 | if (iss >> _bodySize)
405 | {
406 | if (_bodySize <= _config.getNum(BODY_SIZE))
407 | {
408 | if(_headers.find("Content-Type") != _headers.end() \
409 | && _headers["Content-Type"].find("multipart/form-data") != std::string::npos \
410 | && _headers["Content-Type"].find("boundary") != std::string::npos) \
411 | getBoundaries(_bodyPos);
412 | else
413 | {
414 | _body = _request.substr(_bodyPos + 4 , _bodySize);
415 | // if method is POST and the uri doesnt end with .py or .rb
416 | if (_method == "POST" && _URI.find(".py") == std::string::npos && _URI.find(".rb") == std::string::npos)
417 | {
418 | if (isMethodAllowed())
419 | {
420 | std::string fileName = generateRandomString();
421 | std::string uplaodPath = getUploadPath();
422 | creatFile(uplaodPath + fileName, _body);
423 | }
424 | else
425 | _error = 405;
426 | }
427 | }
428 | }
429 | else
430 | _error = 413;
431 | }
432 | else
433 | _error = 400;
434 | }
435 | else if (_headers.find("Transfer-Encoding") != _headers.end() \
436 | && _headers["Transfer-Encoding"].find("chunked") != std::string::npos)
437 | {
438 | getChunkedBody(_bodyPos);
439 | _bodySize = _body.size();
440 | if (_bodySize <= _config.getNum(BODY_SIZE))
441 | {
442 | if (_method == "POST" && _URI.find(".py") == std::string::npos && _URI.find(".rb") == std::string::npos)
443 | {
444 | if (isMethodAllowed())
445 | {
446 | std::string fileName = generateRandomString();
447 | std::string uplaodPath = getUploadPath();
448 | creatFile(uplaodPath + fileName, _body);
449 | }
450 | else
451 | _error = 405;
452 | }
453 | }
454 | else
455 | _error = 413;
456 | }
457 | }
458 | std::string Request::getUploadPath()
459 | {
460 | size_t locationLen = _config.location.size();
461 | std::string uplaodPath = _config.getString(UPLOAD_PATH);
462 | for (size_t i = 0; i < locationLen; i++)
463 | {
464 | if (_URI.find(_config.location[i].getLocationName()) != std::string::npos)
465 | {
466 | uplaodPath = _config.location[i].getString(UPLOAD_PATH);
467 | }
468 | }
469 | return uplaodPath;
470 | }
471 | bool Request::isMethodAllowed()
472 | {
473 | std::vector allowedMethods = _config.getMethods();
474 | size_t locationLen = _config.location.size();
475 | for (size_t i = 0; i < locationLen; i++)
476 | {
477 | if (_URI.find(_config.location[i].getLocationName()) != std::string::npos)
478 | {
479 | allowedMethods = _config.location[i].getMethods();
480 | }
481 | }
482 | if (std::find(allowedMethods.begin(), allowedMethods.end(), _method) != allowedMethods.end())
483 | return true;
484 | return false;
485 | }
486 |
487 | std::string Request::getfileNametodelete()
488 | {
489 | std::string tmpURI = _URI;
490 | std::string fileName = _config.getString(ROOT) + tmpURI;
491 | if (tmpURI == "/")
492 | {
493 | fileName += _config.getString(INDEX);
494 | return fileName;
495 | }
496 | size_t locationLen = _config.location.size();
497 | for (size_t i = 0; i < locationLen; i++)
498 | {
499 | if (tmpURI.find(_config.location[i].getLocationName()) != std::string::npos)
500 | {
501 | fileName = tmpURI.replace(tmpURI.find(_config.location[i].getLocationName()), \
502 | _config.location[i].getLocationName().length(), _config.location[i].getString(ROOT));
503 | if (fileName == _config.location[i].getString(ROOT))
504 | {
505 | std::string index = _config.location[i].getString(INDEX);
506 | if (index != "")
507 | fileName += "/" + index;
508 | else
509 | fileName += "/" + _config.getString(INDEX);
510 | }
511 | }
512 | }
513 | return fileName;
514 | }
515 | void Request::HandelDeleteMethod()
516 | {
517 | if (_method == "DELETE")
518 | {
519 | if (isMethodAllowed())
520 | {
521 | std::string fileName = getfileNametodelete();
522 | if (remove(fileName.c_str()) != 0)
523 | _error = 404;
524 | else
525 | _error = 204;
526 | }
527 | else
528 | _error = 405;
529 | }
530 | }
531 |
532 | void freeBoundaryBody(BoundaryBody *head)
533 | {
534 | BoundaryBody *tmp;
535 | while (head != NULL)
536 | {
537 | tmp = head;
538 | head = head->next;
539 | delete tmp;
540 | }
541 | }
542 | Request::~Request()
543 | {
544 | // std::cout << "Request destroyed" << std::endl;
545 | freeBoundaryBody(_boundaryBody);
546 | _headers.clear();
547 | _queries.clear();
548 | _cookies.clear();
549 | }
--------------------------------------------------------------------------------
/src/request/RequestUtils.cpp:
--------------------------------------------------------------------------------
1 | #include "Request.hpp"
2 |
3 | Request::Request(const Request & other) {
4 | *this = other;
5 | }
6 | Request &Request::operator=(const Request & other) {
7 | if (this != &other) {
8 | this->_REQ.write(other._REQ.str().c_str(), other._REQ.str().size());
9 | this->_request = other._request;
10 | this->_requestLength = other._requestLength;
11 | this->_httpVersion = other._httpVersion;
12 | this->_body = other._body;
13 | this->_URI = other._URI;
14 | this->_method = other._method;
15 | this->_queries = other._queries;
16 | this->_boundary = other._boundary;
17 | this->_boundaryBody = other._boundaryBody;
18 | this->_headers = other._headers;
19 | this->_cookies = other._cookies;
20 | this->_config = other._config;
21 | this->_servers = other._servers;
22 | this->_bodySize = other._bodySize;
23 | this->_error = other._error;
24 | this->_keepAlive = other._keepAlive;
25 | this->_isHeadersRead = other._isHeadersRead;
26 | this->_clientSocket = other._clientSocket;
27 | this->_isBodyRead = other._isBodyRead;
28 | this->_checkBoundary = other._checkBoundary;
29 | }
30 | return *this;
31 | }
32 |
33 | std::string Request::getFullRequest() const
34 | {
35 | return this->_request;
36 | }
37 |
38 | size_t Request::getRequestLength() const
39 | {
40 | return this->_requestLength;
41 | }
42 |
43 | std::string Request::getBody() const
44 | {
45 | return this->_body;
46 | }
47 |
48 | std::string Request::getHTTPVersion() const
49 | {
50 | return this->_httpVersion;
51 | }
52 |
53 | std::string Request::getPath() const
54 | {
55 | return this->_URI;
56 | }
57 |
58 | std::string Request::getMethod() const
59 | {
60 | return this->_method;
61 | }
62 |
63 | std::string Request::getQueries() const
64 | {
65 | return this->_queries;
66 | }
67 |
68 | bool Request::KeepAlive() const
69 | {
70 | return this->_keepAlive;
71 | }
72 |
73 | std::map Request::getHeaders() const
74 | {
75 | return this->_headers;
76 | }
77 |
78 |
79 | bool Request::isHeadersRead() const {
80 | return _isHeadersRead;
81 | }
82 |
83 | bool Request::isBodyRead() const {
84 | return _isBodyRead;
85 | }
86 |
87 | size_t Request::getBodyLength(std::string Content_length)
88 | {
89 | std::string bodylength = "";
90 | int i = 0;
91 | while (Content_length[i] != '\r' && std::isdigit(Content_length[i]))
92 | {
93 | bodylength += Content_length[i];
94 | i++;
95 | }
96 | std::istringstream converter(bodylength);
97 | long long bodyLength;
98 | if (converter >> bodyLength)
99 | return bodyLength;
100 | else {
101 | _error = 400;
102 | return 0;
103 | };
104 | }
105 |
106 | int hexStringToInt(const std::string hexString) {
107 | std::istringstream converter(hexString);
108 | int intValue;
109 | converter >> std::hex >> intValue;
110 | return intValue;
111 | }
112 |
--------------------------------------------------------------------------------
/src/response/ReponseUtils.cpp:
--------------------------------------------------------------------------------
1 | # include
2 | # include "Macros.hpp"
3 | # include
4 | # include "Config.hpp"
5 | # include
6 | std::string constructFilePath(const std::string& requestPath, const std::string &root, const std::string &index) {
7 | std::string pathWithoutQuery = requestPath;
8 | if (pathWithoutQuery.empty() || pathWithoutQuery[0] != '/') {
9 | pathWithoutQuery = "/" + pathWithoutQuery;
10 | }
11 | if (!pathWithoutQuery.empty() && pathWithoutQuery[pathWithoutQuery.length() - 1] == '/') {
12 | pathWithoutQuery += index;
13 | }
14 | // Check if there is no extension by finding the last dot in the string
15 | else if (pathWithoutQuery.find_last_of('.') == std::string::npos) {
16 | pathWithoutQuery += "/" + index;
17 | }
18 |
19 | std::string filePath = root + pathWithoutQuery;
20 | return filePath;
21 | }
22 |
23 |
24 | std::string getContentType(std::string filename) {
25 | std::string extension;
26 | if (filename.empty()) {
27 | return "text/html";
28 | }
29 | try {
30 | extension = filename.substr(filename.find_last_of('.'));
31 | }
32 | catch (std::exception &e) {
33 | return "text/html";
34 | }
35 | if (mimeTypes.find(extension) != mimeTypes.end()) {
36 | return mimeTypes[extension];
37 | }
38 | return "text/html";
39 | }
40 |
41 | bool isDirectory(const char* path) {
42 | struct stat fileInfo;
43 |
44 | if (stat(path, &fileInfo) != 0) {
45 | return false;
46 | }
47 |
48 | return S_ISDIR(fileInfo.st_mode);
49 | }
--------------------------------------------------------------------------------
/src/response/Response.cpp:
--------------------------------------------------------------------------------
1 | #include "Response.hpp"
2 | #include
3 | #include
4 | #include
5 | #include
6 | Response::Response() :
7 | _protocol(""),
8 | _status_code(""),
9 | _body(""),
10 | _headers()
11 | {
12 | _dataSent = 0;
13 | _fd = 0;
14 | _offset = 0;
15 | _isCGI = false;
16 | _filePath = "";
17 | _buffer = "";
18 | _clientSocket = -1;
19 | _headersSent = false;
20 | }
21 |
22 | Response::Response(int clientSocket)
23 | : _protocol(""),
24 | _status_code(""),
25 | _body(""),
26 | _headers()
27 | {
28 | _dataSent = 0;
29 | _fd = 0;
30 | _offset = 0;
31 | _isCGI = false;
32 | _filePath = "";
33 | _buffer = "";
34 | _clientSocket = clientSocket;
35 | _headersSent = false;
36 | }
37 |
38 | Response::Response(int clientSocket, std::vector &servers)
39 | : _protocol(""),
40 | _status_code(""),
41 | _body(""),
42 | _headers()
43 | {
44 | _dataSent = 0;
45 | _fd = 0;
46 | _offset = 0;
47 | _isCGI = false;
48 | _filePath = "";
49 | _buffer = "";
50 | _clientSocket = clientSocket;
51 | _headersSent = false;
52 | _servers = servers;
53 | _error = 0;
54 | _autoindex = false;
55 | _isTextStream = false;
56 | _redirect = "";
57 | }
58 |
59 | Response::~Response()
60 | {
61 | }
62 |
63 | std::string Response::findStatusCode(int code)
64 | {
65 | std::string status_code;
66 | switch (code)
67 | {
68 | case 200:
69 | status_code = "200 OK";
70 | break;
71 | case 201:
72 | status_code = "201 Created";
73 | break;
74 | case 204:
75 | status_code = "204 No Content";
76 | break;
77 | case 403:
78 | status_code = "403 Forbidden";
79 | break;
80 | case 400:
81 | status_code = "400 Bad Request";
82 | break;
83 | case 301:
84 | status_code = "301 Moved Permanently";
85 | break;
86 | case 302:
87 | status_code = "302 Moved Temporarily";
88 | break;
89 | case 404:
90 | status_code = "404 Not Found";
91 | break;
92 | case 405:
93 | status_code = "405 Method Not Allowed";
94 | break;
95 | case 413:
96 | status_code = "413 Payload Too Large";
97 | break;
98 | case 500:
99 | status_code = "500 Internal Server Error";
100 | break;
101 | case 501:
102 | status_code = "501 Not Implemented";
103 | break;
104 | case 505:
105 | status_code = "505 HTTP Version Not Supported";
106 | break;
107 | default:
108 | break;
109 | }
110 | return status_code;
111 | }
112 |
113 | /**
114 | * @brief Finds the appropriate location block for the given request and sets the corresponding response properties.
115 | *
116 | * @param req The request object.
117 | * @return int Returns CONTINUE if the request can be processed, otherwise returns an error code.
118 | */
119 | int Response::findRouting(Request &req)
120 | {
121 | std::vector &locations = _config.location;
122 |
123 | for (std::vector::iterator it = locations.begin(); it != locations.end(); ++it)
124 | {
125 | if (req.getPath().find(it->getLocationName()) == 0)
126 | {
127 | _location = *it;
128 | _root = it->getString(ROOT);
129 | _index = it->getString(INDEX);
130 | _autoindex = it->getAutoindex();
131 | _errorPages = it->getErrorPages();
132 | _methods = it->getMethods();
133 | _redirect = it->getReturned();
134 |
135 | int idx = req.getPath().find(it->getLocationName());
136 | std::string locationName = it->getLocationName();
137 | std::string relativePath = (req.getPath().find_first_of(locationName) == 0) ?
138 | req.getPath().substr(locationName.length()) :
139 | req.getPath().substr(0, idx);
140 |
141 | _filePath = constructFilePath(relativePath, _root, "");
142 | if (isDirectory(_filePath.c_str()))
143 | _filePath = constructFilePath(relativePath, _root, _index);
144 |
145 | if (_filePath[_filePath.length() - 1] == '/')
146 | _filePath = _filePath.substr(0, _filePath.length() - 1);
147 |
148 | if (!_redirect.empty() && (req.getPath().substr(it->getLocationName().length()) == "/" ||
149 | req.getPath().substr(it->getLocationName().length()) == "") &&
150 | req.getMethod() == "GET")
151 | {
152 | _error = 301;
153 | return _error;
154 | }
155 |
156 | if (_autoindex && _index.empty())
157 | {
158 | _filePath = constructFilePath(relativePath, _root, "");
159 | if (isDirectory(_filePath.c_str()))
160 | _filePath = constructFilePath(relativePath, _root, _index);
161 | if (_filePath[_filePath.length() - 1] == '/') {
162 | _filePath = _filePath.substr(0, _filePath.length() - 1);
163 | }
164 | _isTextStream = true;
165 | return CONTINUE;
166 | }
167 |
168 | return CONTINUE;
169 | }
170 | }
171 |
172 | _root = _config.getString(ROOT);
173 | _index = _config.getString(INDEX);
174 | _autoindex = _config.getAutoindex();
175 | _errorPages = _config.getErrorPages();
176 | _methods = _config.getMethods();
177 |
178 | _filePath = constructFilePath(req.getPath(), _root, "");
179 | if (isDirectory(_filePath.c_str()))
180 | _filePath = constructFilePath(req.getPath(), _root, _index);
181 | if (_filePath[_filePath.length() - 1] == '/')
182 | _filePath = _filePath.substr(0, _filePath.length() - 1);
183 | if (_autoindex && _index.empty())
184 | {
185 | _filePath = constructFilePath(req.getPath(), _root, "");
186 | if (isDirectory(_filePath.c_str()))
187 | _filePath = constructFilePath(req.getPath(), _root, _index);
188 | if (_filePath[_filePath.length() - 1] == '/') {
189 | _filePath = _filePath.substr(0, _filePath.length() - 1);
190 | }
191 | _isTextStream = true;
192 | return CONTINUE;
193 | }
194 |
195 | return CONTINUE;
196 | }
197 |
198 |
199 | /**
200 | * @brief Handles default error by setting the status code, body, headers and initializing headers.
201 | *
202 | * @param code The error code.
203 | */
204 | void Response::handleDefaultError(int code)
205 | {
206 | std::stringstream ss;
207 |
208 | _status_code = findStatusCode(code);
209 | ss << "" << _status_code << "
";
210 | _body = ss.str();
211 | _fileSize = _body.size();
212 | _headers["Content-Type"] = "text/html";
213 | _isTextStream = true;
214 | InitHeaders(_request);
215 | }
216 |
217 | void Response::handleError(int code)
218 | {
219 | if (code == 301)
220 | {
221 | _status_code = findStatusCode(code);
222 | _fileSize = 0;
223 | _isTextStream = true;
224 | InitHeaders(_request);
225 | }
226 | else
227 | {
228 | _status_code = findStatusCode(code);
229 | std::string errorPage = "";
230 | if (_errorPages.empty())
231 | {
232 | if (_location.getErrorPages().empty())
233 | {
234 | if (_config.getErrorPages().empty())
235 | {
236 | handleDefaultError(code);
237 | return;
238 | }
239 | else
240 | {
241 | _errorPages = _config.getErrorPages();
242 | }
243 | }
244 | else
245 | {
246 | _errorPages = _location.getErrorPages();
247 | }
248 | }
249 | if (_errorPages.find(code)->second.find("/") == 0)
250 | errorPage = _errorPages.find(code)->second;
251 | else {
252 | errorPage = constructFilePath(_errorPages.find(code)->second, _root, "");
253 | }
254 | if (isDirectory(errorPage.c_str())) {
255 | handleDefaultError(code);
256 | return;
257 | }
258 |
259 | _fd = open(errorPage.c_str(), O_RDONLY);
260 | if (_fd == -1)
261 | {
262 | handleDefaultError(code);
263 | return;
264 | }
265 | _fileSize = lseek(_fd, 0, SEEK_END);
266 | lseek(_fd, 0, SEEK_SET);
267 | _isTextStream = false;
268 | }
269 | InitHeaders(_request);
270 | }
271 |
272 | std::string findDirname(const std::string& path, const std::string& root)
273 | {
274 | // Find the position where root ends in the path
275 | size_t rootPos = path.find(root);
276 | if (rootPos == std::string::npos)
277 | return "";
278 |
279 | // Remove root from the path
280 | std::string dirname = path.substr(rootPos + root.length());
281 |
282 | // Find the last '/' character in the remaining path
283 | size_t pos = dirname.find_last_of('/');
284 | if (pos == std::string::npos)
285 | return "";
286 |
287 | // Extract the dirname
288 | dirname = dirname.substr(0, pos);
289 |
290 | return dirname;
291 | }
292 |
293 |
294 | void Response::genListing()
295 | {
296 | std::string path = _filePath;
297 | DIR *dir = opendir(path.c_str());
298 |
299 | if (dir == NULL)
300 | {
301 | _error = ISFILE;
302 | return;
303 | }
304 |
305 | std::stringstream ss;
306 | std::string location = _location.getLocationName();
307 | std::string locationRoot = _location.getString(ROOT);
308 | std::string relativePath = path.substr(locationRoot.length());
309 | std::string pathName = location + relativePath;
310 | if (pathName[pathName.length() - 1] != '/')
311 | pathName += "/";
312 | ss << "Index of " << pathName << "Index of " << pathName << "
";
313 |
314 | struct dirent *ent;
315 | while ((ent = readdir(dir)) != NULL)
316 | {
317 | ss << "d_name << "\">" << ent->d_name << "
";
318 | }
319 |
320 | closedir(dir);
321 |
322 | ss << "
";
323 | _body = ss.str();
324 | _fileSize = _body.size();
325 | _isTextStream = true;
326 | InitHeaders(_request);
327 |
328 | }
329 |
330 |
331 | void Response::InitFile(Request &req) {
332 | int routing = findRouting(req);
333 | _error = req.getError();
334 | if (_error != 0) {
335 | throw HTTPException("Request error", _error);
336 | }
337 | if (checkMethod(req) == ERROR)
338 | throw HTTPException("Method not allowed", 405);
339 | if (checkHttpVersion(req) == ERROR)
340 | throw HTTPException("HTTP version not supported", 505);
341 | if (routing == 301) {
342 | throw HTTPException("Moved Temporarily", 302);
343 | }
344 |
345 | if (routing == 404 || (isDirectory(_filePath.c_str()) && _autoindex == false)) {
346 | _error = 404;
347 | throw HTTPException("Not Found", 404);
348 | }
349 |
350 |
351 | if (_autoindex && _isTextStream) {
352 | genListing();
353 | if (_error != ISFILE) {
354 |
355 | return;
356 | }
357 | _isTextStream = false;
358 | _error = 0;
359 | }
360 | _fd = open(_filePath.c_str(), O_RDONLY);
361 | if (_fd == -1 && _error != ISFILE)
362 | throw HTTPException("Not Found", 404);
363 | _fileSize = lseek(_fd, 0, SEEK_END);
364 | lseek(_fd, 0, SEEK_SET);
365 | InitHeaders(req);
366 | }
367 |
368 |
369 | int Response::checkMethod(Request &req)
370 | {
371 | std::string const &method = req.getMethod();
372 | if (_methods.empty())
373 | {
374 | _methods.push_back("GET");
375 | _methods.push_back("POST");
376 | _methods.push_back("DELETE");
377 | }
378 | for (std::vector::iterator it = _methods.begin(); it != _methods.end(); it++)
379 | {
380 | if (*it == method)
381 | return CONTINUE;
382 | }
383 | _error = 405;
384 | throw HTTPException("Method not allowed", 405);
385 | return ERROR;
386 | }
387 |
388 | int Response::checkHttpVersion(Request &req)
389 | {
390 | std::string const &version = req.getHTTPVersion();
391 | if (version != "HTTP/1.1")
392 | {
393 | _error = 505;
394 | throw HTTPException("HTTP version not supported", 505);
395 | return ERROR;
396 | }
397 | return CONTINUE;
398 | }
399 |
400 | void Response::InitHeaders(Request &req)
401 | {
402 | std::stringstream ss;
403 |
404 | _headers["Server"] = "Webserv/1.0";
405 |
406 | // Check if Content-Type is not set
407 | if (_headers.find("Content-Type") == _headers.end())
408 | {
409 | _headers["Content-Type"] = getContentType(_filePath);
410 | }
411 |
412 | // Check _isTextStream and _redirect
413 | if (_isTextStream == true && _redirect.empty())
414 | {
415 | ss << _body.length();
416 | _headers["Content-Length"] = ss.str();
417 | _headers["Connection"] = req.isKeepAlive() ? "keep-alive" : "close";
418 | return;
419 | }
420 |
421 | // Check _redirect
422 | if (!_redirect.empty())
423 | {
424 | _headers["Location"] = _redirect;
425 | _error = 301;
426 | }
427 |
428 | // Set Content-Length and Connection
429 | ss << _fileSize;
430 | _headers["Content-Length"] = ss.str();
431 | _headers["Connection"] = req.isKeepAlive() ? "keep-alive" : "close";
432 | }
433 |
434 | #ifdef __APPLE__
435 | int Response::sendFileData()
436 | {
437 | off_t bytesSent = 2048;
438 | int res = sendfile(_fd, _clientSocket, _offset, &bytesSent, NULL, 0);
439 | if (res == -1 && _offset >= _fileSize)
440 | {
441 | close(_fd);
442 | return DONE;
443 | }
444 | _offset += bytesSent;
445 | if (_offset >= _fileSize)
446 | {
447 | if (_fd > 0)
448 | {
449 | close(_fd);
450 | return DONE;
451 | }
452 | }
453 | return CONTINUE;
454 | }
455 | #else
456 | #include
457 |
458 | int Response::sendFileData()
459 | {
460 | off_t offset = _offset; // Save the offset before modifying it
461 | off_t remainingBytes = _fileSize - offset;
462 |
463 | ssize_t bytesSent = sendfile(_clientSocket, _fd, &offset, remainingBytes);
464 | if (bytesSent == -1)
465 | {
466 | return ERROR;
467 | }
468 | else if (bytesSent == 0 && offset >= _fileSize)
469 | {
470 | close(_fd);
471 | _fd = -1;
472 | _offset = 0;
473 | return DONE;
474 | }
475 |
476 | _offset = offset;
477 | if (_offset >= _fileSize)
478 | {
479 | close(_fd);
480 | _fd = -1;
481 | _offset = 0;
482 | return DONE;
483 | }
484 | return CONTINUE;
485 | }
486 | #endif
487 |
488 | int Response::sendTextData()
489 | {
490 | std::string &body = _body;
491 | ssize_t remainingBytes = body.length() - _dataSent;
492 | ssize_t bytesSent = send(_clientSocket, body.c_str() + _dataSent, remainingBytes, 0);
493 | _dataSent += bytesSent;
494 | if (bytesSent == -1)
495 | {
496 | return ERROR;
497 | }
498 | else if (bytesSent == 0 && _dataSent >= body.length())
499 | {
500 | _dataSent = 0;
501 | return DONE;
502 | }
503 | _dataSent += bytesSent;
504 | if (_dataSent >= body.length())
505 | {
506 | _dataSent = 0;
507 | return DONE;
508 | }
509 | return CONTINUE;
510 | }
511 |
512 | void Response::findConfig(Request &req)
513 | {
514 | const std::map &headers = req.getHeaders();
515 | if (headers.find("Host") != headers.end())
516 | {
517 | std::string host = headers.find("Host")->second;
518 | int port = req.getPort();
519 | for (std::vector::iterator it = _servers.begin(); it != _servers.end(); it++)
520 | {
521 | if (it->getString(SERVER_NAME) == host || it->getString(SERVER_NAME) + ":" + std::to_string(port) == host)
522 | {
523 | _config = *it;
524 | return;
525 | }
526 | }
527 | }
528 | _config = _servers[0];
529 | }
530 |
531 |
532 | void Response::handleCGI() {
533 | if (_cgi && _cgi->isCgiDone() == true)
534 | {
535 | _fd = _cgi->getFd();
536 | _error = _cgi->getError();
537 | if (_error != 0)
538 | throw HTTPException("CGI error", _error);
539 | else {
540 | _isCGI = true;
541 | _fileSize = lseek(_fd, 0, SEEK_END);
542 | lseek(_fd, 0, SEEK_SET);
543 | _headers = _cgi->getHeaders();
544 | _headers["Content-Length"] = std::to_string(_fileSize);
545 | std::vector cookies = _cgi->getCookies();
546 | for (std::vector::iterator it = cookies.begin(); it != cookies.end(); it++)
547 | {
548 | _headers["Set-Cookie"] = *it;
549 | }
550 | _headers["Connection"] = _request.isKeepAlive() ? "keep-alive" : "close";
551 | }
552 | }
553 | }
554 |
555 | int Response::sendResp(Request &req, CGI *cgi)
556 | {
557 | try {
558 | findConfig(req);
559 | _cgi = cgi;
560 | handleCGI();
561 | if (_fd == 0 && _isCGI == false) {
562 | InitFile(req);
563 | }
564 | }
565 | catch (HTTPException &e) {
566 | _error = e.getErrorCode();
567 | handleError(e.getErrorCode());
568 |
569 | }
570 | catch (std::exception &e) {
571 | _error = 500;
572 | handleError(_error);
573 |
574 | }
575 | catch (...) {
576 | _error = 500;
577 | handleError(_error);
578 | }
579 | std::stringstream ss;
580 | if (_headersSent == false)
581 | {
582 | if (_error == 0)
583 | _status_code = findStatusCode(200);
584 | ss << "HTTP/1.1 " << _status_code << "\r\n";
585 | for (std::map::iterator it = _headers.begin(); it != _headers.end(); it++)
586 | {
587 | ss << it->first << ": " << it->second << "\r\n";
588 | }
589 | ss << "\r\n";
590 | _buffer = ss.str();
591 | int res = send(_clientSocket, _buffer.c_str(), _buffer.length(), 0);
592 | if (res == -1)
593 | _buffer = "";
594 | if (res == 0 && _buffer.length() == 0)
595 | _buffer = "";
596 | _buffer = "";
597 | _headersSent = true;
598 | }
599 | if (_redirect.empty() == false)
600 | return DONE;
601 | if (_isTextStream)
602 | return sendTextData();
603 | else
604 | return sendFileData();
605 | return CONTINUE;
606 |
607 | }
--------------------------------------------------------------------------------
/src/server/server.cpp:
--------------------------------------------------------------------------------
1 | # include "Server.hpp"
2 | # include "CGIHandler.hpp"
3 | # include
4 | # include