Multiples minors changes (cgi env, comment, ...)
This commit is contained in:
107
srcs/Client.cpp
107
srcs/Client.cpp
@@ -75,10 +75,12 @@ Client & Client::operator=( Client const & rhs )
|
||||
* PUBLIC MEMBER FUNCTIONS
|
||||
*********************************************/
|
||||
|
||||
// http headers :
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
|
||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||
/* HTTP Headers :
|
||||
https://www.iana.org/assignments/http-fields/http-fields.xhtml
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
|
||||
https://www.tutorialspoint.com/http/http_requests.htm
|
||||
*/
|
||||
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||
{
|
||||
if (raw_request.find(CRLF CRLF) == NPOS)
|
||||
@@ -96,8 +98,8 @@ void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||
|
||||
if (status)
|
||||
return;
|
||||
assigned_server = ::_determine_process_server(this, servers);
|
||||
assigned_location = ::_determine_location(*assigned_server, _request.abs_path);
|
||||
assigned_server = _determine_process_server(this, servers);
|
||||
assigned_location = _determine_location(*assigned_server, _request.abs_path);
|
||||
_check_request_errors();
|
||||
if (status)
|
||||
return;
|
||||
@@ -389,6 +391,7 @@ void Client::_parse_request_fields()
|
||||
::str_map_key_tolower(_request.headers);
|
||||
}
|
||||
|
||||
// TODO : I think its now useless. Probably to delete.
|
||||
void Client::_parse_port_hostname(std::string host)
|
||||
{
|
||||
size_t pos;
|
||||
@@ -453,6 +456,98 @@ void Client::_check_request_errors()
|
||||
return;
|
||||
}
|
||||
|
||||
ServerConfig *Client::_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
||||
{
|
||||
/*
|
||||
Behavior like this :
|
||||
http://nginx.org/en/docs/http/request_processing.html
|
||||
*/
|
||||
|
||||
std::string server_name = client->get_rq_headers("Host");
|
||||
std::cerr << "server_name = " << server_name << "\n";
|
||||
size_t pos = server_name.rfind(':');
|
||||
if (pos != NPOS)
|
||||
server_name.erase(pos);
|
||||
std::cerr << "server_name = " << server_name << "\n";
|
||||
|
||||
std::vector<ServerConfig>::iterator it = servers.begin();
|
||||
std::vector<ServerConfig>::iterator default_server = servers.end();
|
||||
|
||||
while (it != servers.end())
|
||||
{
|
||||
if (it->host == client->get_cl_lsocket()->host
|
||||
&& it->port == client->get_cl_lsocket()->port)
|
||||
{
|
||||
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
||||
break;
|
||||
else if (default_server == servers.end())
|
||||
default_server = it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it != servers.end())
|
||||
return (&(*it));
|
||||
else
|
||||
return (&(*default_server));
|
||||
}
|
||||
|
||||
// const?
|
||||
const LocationConfig *Client::_determine_location(const ServerConfig &server, const std::string &path)
|
||||
{
|
||||
/* RULES ***
|
||||
|
||||
If a path coresponds exactly to a location, use that one
|
||||
if no path coresponds then use the most correct one
|
||||
most correct means the most precise branch that is still above
|
||||
the point we are aiming for
|
||||
|
||||
New Rule for location paths, they never end in /
|
||||
Sooo
|
||||
If we get a url that ends in / ignore the last /
|
||||
|
||||
*/
|
||||
|
||||
std::string uri = path;
|
||||
if (uri[uri.size() - 1] == '/' && uri.size() != 1)
|
||||
uri.erase(uri.size() - 1);
|
||||
|
||||
|
||||
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||
{
|
||||
// std::cout << it->path << " -- ";
|
||||
if (it->path.size() > uri.size())
|
||||
continue ;
|
||||
|
||||
if (uri.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
if (it->path.size() == uri.size())
|
||||
return (&(*it));
|
||||
else if (uri[it->path.size()] == '/')
|
||||
return (&(*it));
|
||||
// this works cuz only ever looking for a / burried in a longer path
|
||||
}
|
||||
}
|
||||
return (&(server.locations.back()));
|
||||
|
||||
|
||||
// /test/mdr
|
||||
// /test/mdr/
|
||||
// /test/mdrBST
|
||||
|
||||
/////// More stuff to check this still works with
|
||||
|
||||
// /test/test_
|
||||
// /test/test_/
|
||||
// /test/test_deeper
|
||||
// /test/test_deeper/
|
||||
// /test/test_deepei
|
||||
// /test/test_deepei/
|
||||
// /test/test_deeperi
|
||||
// /test/test_deeper/super_deep/
|
||||
// /test/aaaaaaaaaaa/super_deep/
|
||||
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* OVERLOAD
|
||||
*********************************************/
|
||||
|
||||
@@ -105,15 +105,15 @@ class Client
|
||||
void _parse_chunked_body(size_t pos);
|
||||
void _parse_multipart_body(size_t pos);
|
||||
void _check_request_errors();
|
||||
|
||||
ServerConfig*
|
||||
_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
|
||||
const LocationConfig*
|
||||
_determine_location(const ServerConfig &server, const std::string &path);
|
||||
};
|
||||
|
||||
bool operator==(const Client& lhs, const Client& rhs);
|
||||
bool operator==(const Client& lhs, int fd);
|
||||
bool operator==(int fd, const Client& rhs);
|
||||
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -71,39 +71,53 @@ class Webserv
|
||||
std::map<std::string, std::string> _mime_types;
|
||||
|
||||
// accept.cpp
|
||||
void _accept_connection(listen_socket &lsocket);
|
||||
void _accept_connection(listen_socket &lsocket);
|
||||
std::map<std::string, std::string>
|
||||
_extract_infos(struct sockaddr_in addr);
|
||||
_extract_infos(struct sockaddr_in addr);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
void _request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client, std::string &path);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
// ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
|
||||
// const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
|
||||
std::string _determine_file_extension(const std::string &path) const;
|
||||
// method_get.cpp
|
||||
|
||||
// move later
|
||||
void _response(Client *client);
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client, std::string &path);
|
||||
std::string _replace_url_root(Client *client, std::string path);
|
||||
|
||||
void _get(Client *client, std::string &path);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
void _autoindex(Client *client, const std::string &path);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
// method_get.cpp
|
||||
void _get(Client *client, std::string &path);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
void _autoindex(Client *client, const std::string &path);
|
||||
std::string _determine_file_extension(const std::string &path) const;
|
||||
// method_post.cpp
|
||||
void _post(Client *client, const std::string &path);
|
||||
void _upload_files(Client *client);
|
||||
void _post(Client *client, const std::string &path);
|
||||
void _upload_files(Client *client);
|
||||
// method_delete.cpp
|
||||
void _delete(Client *client, const std::string &path);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
// cgi_script.cpp
|
||||
void _delete(Client *client, const std::string &path);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
// epoll_update.cpp
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||
// signal.cpp
|
||||
void _handle_last_signal();
|
||||
// close.cpp
|
||||
void _close_client(int fd);
|
||||
void _close_all_clients();
|
||||
void _close_all_listen_sockets();
|
||||
void _reopen_lsocket(std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_client(uint32_t events, int fd);
|
||||
// init.cpp
|
||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||
void _listen(int socket_fd, unsigned int max_connections);
|
||||
void _init_http_status_map();
|
||||
void _init_mime_types_map();
|
||||
// timeout.cpp
|
||||
void _timeout();
|
||||
// cgi.cpp
|
||||
bool _is_cgi(Client *client, std::string path);
|
||||
size_t _cgi_pos(Client *client, std::string &path, size_t pos);
|
||||
std::string _exec_cgi(Client *client);
|
||||
@@ -117,26 +131,18 @@ class Webserv
|
||||
void _check_script_fields(Client *client, std::string & output);
|
||||
void _add_script_body_length_header(std::string & output);
|
||||
void _remove_body_leading_empty_lines(std::string & output);
|
||||
// epoll_update.cpp
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||
// signal.cpp
|
||||
void _handle_last_signal();
|
||||
// close.cpp
|
||||
void _close_client(int fd);
|
||||
void _close_all_clients();
|
||||
void _close_all_listen_sockets();
|
||||
void _reopen_lsocket(std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_client(uint32_t events, int fd);
|
||||
// init.cpp
|
||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||
void _listen(int socket_fd, unsigned int max_connections);
|
||||
void _init_http_status_map();
|
||||
void _init_mime_types_map();
|
||||
// timeout.cpp
|
||||
void _timeout();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
HTTP Semantics:
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html
|
||||
https://www.bortzmeyer.org/9110.html
|
||||
https://www.bortzmeyer.org/cours-http-cnam.html
|
||||
HTTP/1.1:
|
||||
https://www.rfc-editor.org/rfc/rfc9112.html
|
||||
https://www.bortzmeyer.org/9112.html
|
||||
CGI:
|
||||
https://www.rfc-editor.org/rfc/rfc3875.html
|
||||
*/
|
||||
|
||||
@@ -21,8 +21,8 @@ void Webserv::_accept_connection(listen_socket &lsocket)
|
||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
infos = _extract_infos(addr);
|
||||
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||
_clients.push_back(client);
|
||||
Client new_client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||
_clients.push_back(new_client);
|
||||
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
/*
|
||||
CGI RFC:
|
||||
https://www.rfc-editor.org/rfc/rfc3875.html
|
||||
*/
|
||||
|
||||
bool Webserv::_is_cgi(Client *client, std::string path)
|
||||
{
|
||||
@@ -99,23 +103,26 @@ std::string Webserv::_dup_env(std::string var, int i)
|
||||
}
|
||||
|
||||
// TODO : verifier que les variables sont corrects
|
||||
/*
|
||||
https://www.rfc-editor.org/rfc/rfc3875#section-4.1
|
||||
*/
|
||||
void Webserv::_set_env_vector(Client *client, std::vector<std::string> &env_vector)
|
||||
{
|
||||
env_vector.push_back(_dup_env("AUTH_TYPE")); // authentification not supporte
|
||||
env_vector.push_back(_dup_env("CONTENT_LENGTH" , client->get_rq_body().size()));
|
||||
env_vector.push_back(_dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type")));
|
||||
env_vector.push_back(_dup_env("GATEWAY_INTERFACE" , "CGI/1.1")); // https://www.rfc-editor.org/rfc/rfc387)
|
||||
env_vector.push_back(_dup_env("PATH_INFO" , client->get_rq_script_info()));
|
||||
env_vector.push_back(_dup_env("PATH_TRANSLATED")); // not supported
|
||||
env_vector.push_back(_dup_env("GATEWAY_INTERFACE" , "CGI/1.1")); // https://www.rfc-editor.org/rfc/rfc3875#section-4.1.4
|
||||
env_vector.push_back(_dup_env("PATH_INFO" , client->get_rq_script_info())); // LUKE: To Check
|
||||
env_vector.push_back(_dup_env("PATH_TRANSLATED")); // not supported // LUKE: Why not supported ?
|
||||
env_vector.push_back(_dup_env("QUERY_STRING" , client->get_rq_query()));
|
||||
env_vector.push_back(_dup_env("REMOTE_ADDR" , client->get_cl_ip()));
|
||||
env_vector.push_back(_dup_env("REMOTE_HOST" , client->get_rq_headers("Host"))); // just tes
|
||||
env_vector.push_back(_dup_env("REMOTE_IDENT")); // authentification not supporte
|
||||
env_vector.push_back(_dup_env("REMOTE_USER")); // authentification not supporte
|
||||
env_vector.push_back(_dup_env("REMOTE_HOST" , client->get_cl_ip())); // equal to REMOTE_ADDR or empty
|
||||
env_vector.push_back(_dup_env("REMOTE_IDENT")); // authentification not supported
|
||||
env_vector.push_back(_dup_env("REMOTE_USER")); // authentification not supported
|
||||
env_vector.push_back(_dup_env("REQUEST_METHOD" , client->get_rq_method_str()));
|
||||
env_vector.push_back(_dup_env("SCRIPT_NAME" , client->get_rq_script_path()));
|
||||
env_vector.push_back(_dup_env("SERVER_NAME" , client->get_rq_hostname()));
|
||||
env_vector.push_back(_dup_env("SERVER_PORT" , client->get_rq_port()));
|
||||
env_vector.push_back(_dup_env("SCRIPT_NAME" , client->get_rq_script_path())); // LUKE: To Check
|
||||
env_vector.push_back(_dup_env("SERVER_NAME" , client->get_cl_lsocket()->host));
|
||||
env_vector.push_back(_dup_env("SERVER_PORT" , client->get_cl_lsocket()->port));
|
||||
env_vector.push_back(_dup_env("SERVER_PROTOCOL" , "HTTP/1.1"));
|
||||
env_vector.push_back(_dup_env("SERVER_SOFTWARE" , "Webserv/0.1"));
|
||||
env_vector.push_back(_dup_env("REDIRECT_STATUS" , "200"));
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#ifndef HTTP_STATUS_HPP
|
||||
# define HTTP_STATUS_HPP
|
||||
|
||||
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
/*
|
||||
https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
*/
|
||||
|
||||
/*
|
||||
First version of macro HTML_ERROR(STATUS) dont work with call like this :
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
std::string Webserv::_replace_url_root(Client *client, std::string path)
|
||||
{
|
||||
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
||||
std::cerr << "path before = " << path << "\n"; // DEBUG
|
||||
if (client->assigned_location->path == "/")
|
||||
path.insert(0, client->assigned_location->root);
|
||||
else
|
||||
path.replace(0, client->assigned_location->path.size(), client->assigned_location->root);
|
||||
std::cerr << "path after = " << path << "\n"; // DEBUG
|
||||
return path;
|
||||
}
|
||||
#define MAX_FILESIZE 1 * MB // unused
|
||||
|
||||
// const?
|
||||
void Webserv::_get(Client *client, std::string &path)
|
||||
{
|
||||
/*
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-get
|
||||
*/
|
||||
void Webserv::_get(Client *client, std::string &path)
|
||||
{
|
||||
std::cout << "_get()\n";
|
||||
if (eval_file_type(path) == IS_DIR)
|
||||
{
|
||||
@@ -42,14 +31,13 @@ void Webserv::_get(Client *client, std::string &path)
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
# define MAX_FILESIZE 1 * MB // unused
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
std::ios::binary
|
||||
https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html#std.io.filestreams.binary
|
||||
tldr : its seems to not be so simple to do read/write of binary file in a portable way.
|
||||
*/
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
std::stringstream buf;
|
||||
|
||||
@@ -155,3 +143,11 @@ void Webserv::_autoindex(Client *client, const std::string &path)
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Webserv::_determine_file_extension(const std::string &path) const
|
||||
{
|
||||
size_t dot_pos = path.rfind(".");
|
||||
if (dot_pos != NPOS && dot_pos + 1 < path.size())
|
||||
return ( path.substr(dot_pos + 1) );
|
||||
return (std::string(""));
|
||||
}
|
||||
|
||||
@@ -119,6 +119,21 @@ void Webserv::_process_method(Client *client, std::string &path)
|
||||
}
|
||||
}
|
||||
|
||||
std::string Webserv::_replace_url_root(Client *client, std::string path)
|
||||
{
|
||||
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
||||
std::cerr << "path before = " << path << "\n"; // DEBUG
|
||||
if (client->assigned_location->path == "/")
|
||||
path.insert(0, client->assigned_location->root);
|
||||
else
|
||||
path.replace(0, client->assigned_location->path.size(), client->assigned_location->root);
|
||||
std::cerr << "path after = " << path << "\n"; // DEBUG
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
https://www.rfc-editor.org/rfc/rfc9112.html#name-status-line
|
||||
*/
|
||||
void Webserv::_insert_status_line(Client *client)
|
||||
{
|
||||
std::string status_line;
|
||||
@@ -176,111 +191,3 @@ void Webserv::_append_body(Client *client, const std::string &body, const std::s
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
}
|
||||
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
||||
{
|
||||
/*
|
||||
Behavior like this :
|
||||
http://nginx.org/en/docs/http/request_processing.html
|
||||
*/
|
||||
|
||||
std::string server_name = client->get_rq_headers("Host");
|
||||
std::cerr << "server_name = " << server_name << "\n";
|
||||
size_t pos = server_name.rfind(':');
|
||||
if (pos != NPOS)
|
||||
server_name.erase(pos);
|
||||
std::cerr << "server_name = " << server_name << "\n";
|
||||
|
||||
std::vector<ServerConfig>::iterator it = servers.begin();
|
||||
std::vector<ServerConfig>::iterator default_server = servers.end();
|
||||
|
||||
while (it != servers.end())
|
||||
{
|
||||
if (it->host == client->get_cl_lsocket()->host
|
||||
&& it->port == client->get_cl_lsocket()->port)
|
||||
{
|
||||
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
||||
break;
|
||||
else if (default_server == servers.end())
|
||||
default_server = it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it != servers.end())
|
||||
return (&(*it));
|
||||
else
|
||||
return (&(*default_server));
|
||||
}
|
||||
|
||||
// const?
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
// is it still TMP Global Scope?
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path)
|
||||
{
|
||||
/* RULES ***
|
||||
|
||||
If a path coresponds exactly to a location, use that one
|
||||
if no path coresponds then use the most correct one
|
||||
most correct means the most precise branch that is still above
|
||||
the point we are aiming for
|
||||
|
||||
New Rule for location paths, they never end in /
|
||||
Sooo
|
||||
If we get a url that ends in / ignore the last /
|
||||
|
||||
*/
|
||||
|
||||
std::string uri = path;
|
||||
if (uri[uri.size() - 1] == '/' && uri.size() != 1)
|
||||
uri.erase(uri.size() - 1);
|
||||
|
||||
|
||||
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||
{
|
||||
// std::cout << it->path << " -- ";
|
||||
if (it->path.size() > uri.size())
|
||||
continue ;
|
||||
|
||||
if (uri.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
if (it->path.size() == uri.size())
|
||||
return (&(*it));
|
||||
else if (uri[it->path.size()] == '/')
|
||||
return (&(*it));
|
||||
// this works cuz only ever looking for a / burried in a longer path
|
||||
}
|
||||
}
|
||||
return (&(server.locations.back()));
|
||||
|
||||
|
||||
// /test/mdr
|
||||
// /test/mdr/
|
||||
// /test/mdrBST
|
||||
|
||||
/* More stuff to check this still works with ***
|
||||
|
||||
/test/test_
|
||||
/test/test_/
|
||||
/test/test_deeper
|
||||
/test/test_deeper/
|
||||
/test/test_deepei
|
||||
/test/test_deepei/
|
||||
/test/test_deeperi
|
||||
/test/test_deeper/super_deep/
|
||||
/test/aaaaaaaaaaa/super_deep/
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Webserv::_determine_file_extension(const std::string &path) const
|
||||
{
|
||||
size_t dot_pos = path.rfind(".");
|
||||
if (dot_pos != NPOS && dot_pos + 1 < path.size())
|
||||
return ( path.substr(dot_pos + 1) );
|
||||
return (std::string(""));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user