├── .gitignore ├── README.md ├── doc ├── bgnet_a4_bw_2.pdf └── doc.md ├── intra ├── cgi_tester ├── disassembletester ├── tester ├── ubuntu_cgi_tester └── ubuntu_tester ├── project └── v1 │ ├── Makefile │ ├── commit.sh │ ├── conf │ ├── tester.conf │ └── ubuntu_tester.conf │ ├── includes │ ├── Client.hpp │ ├── Code.hpp │ ├── Colors.hpp │ ├── Conf.hpp │ ├── Config_parser.hpp │ ├── Headers.hpp │ ├── Location.hpp │ ├── Logger.hpp │ ├── Request.hpp │ ├── Response.hpp │ ├── Server.hpp │ ├── Utils.hpp │ └── utils_tmp.hpp │ ├── other_tests │ ├── siege.sh │ └── stress.sh │ ├── srcs │ ├── CGI.cpp │ ├── Client.cpp │ ├── Conf.cpp │ ├── Config_parser.cpp │ ├── Logger.cpp │ ├── Response.cpp │ ├── Server.cpp │ ├── Utils.cpp │ ├── get_next_line │ │ ├── get_next_line.cpp │ │ ├── get_next_line.hpp │ │ └── get_next_line_utils.cpp │ ├── main.cpp │ ├── methods │ │ ├── connect.cpp │ │ ├── delete.cpp │ │ ├── get.cpp │ │ ├── option.cpp │ │ ├── post.cpp │ │ ├── put.cpp │ │ └── trace.cpp │ ├── request │ │ ├── Debug.cpp │ │ ├── Parsing.cpp │ │ └── Request.cpp │ └── utils_tmp.cpp │ ├── tests │ ├── deleteTests.py │ ├── getTests.py │ ├── headTests.py │ ├── optionsTests.py │ ├── postTests.py │ ├── printReqAndResp.py │ └── putTests.py │ └── www │ ├── CGI │ ├── cgi.sh │ ├── cgi_tester │ ├── perl_test │ ├── test_cgi_env.cgi │ └── ubuntu_cgi_tester │ ├── methods │ ├── post │ │ ├── TEST4 │ │ ├── bj │ │ ├── index.html │ │ ├── post_body │ │ ├── test.cgi │ │ └── text │ └── put │ │ ├── abc │ │ └── file │ ├── perso │ ├── TEST4 │ ├── bj │ ├── delete │ │ └── to_delete │ ├── error │ │ ├── 400.html │ │ ├── 401.html │ │ ├── 404.html │ │ ├── 405.html │ │ ├── 413.html │ │ └── 503.html │ ├── index.cgi │ ├── index.html │ ├── post_body │ ├── test.cgi │ ├── test │ │ ├── bonjour │ │ ├── index.html │ │ ├── index.html.en │ │ ├── index.html.fr │ │ ├── index.html.fr.utf8 │ │ ├── index.html.iso-8859-1 │ │ └── index.html.utf8 │ ├── text │ └── vide │ │ ├── listing1 │ │ ├── listing2 │ │ └── listing3 │ └── tester │ ├── CGI │ ├── cgi_tester │ ├── perl_test │ └── test_cgi_env.cgi │ ├── YoupiBanane │ ├── Yeah │ │ └── not_happy.bad_extension │ ├── nop │ │ ├── other.pouic │ │ └── youpi.bad_extension │ ├── youpi.bad_extension │ └── youpi.bla │ ├── abc │ ├── error │ ├── 400.html │ ├── 401.html │ ├── 404.html │ ├── 405.html │ ├── 413.html │ └── 503.html │ ├── file │ ├── getindex.html │ └── index.html ├── subject └── webserv.pdf └── utils ├── commit.sh └── memory.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .dSYM 3 | 4 | # OS generated files # 5 | ###################### 6 | .DS_Store 7 | .DS_Store? 8 | ._* 9 | .Spotlight-V100 10 | .Trashes 11 | ehthumbs.db 12 | Thumbs.db 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 42 mandatory project 2 | - project name: webserv 3 | - Teamed with 42 students [ylegzouli](https://github.com/ylegzouli) and [flavienfr](https://github.com/flavienfr) 🚀 4 | - Summary: *"This project is here to make you write your own HTTP server. You will follow the real HTTP RFC and you will be able to test it with a real browser. HTTP is one of the most used protocol on internet. Knowing its arcane will be useful, even if you won’t be working on a website."* 5 | 6 | # 1. usage (mac OS) 7 | 8 | ``` 9 | cd project/v1 && make launch 10 | ``` 11 | in another shell: 12 | ``` 13 | cd project/v1 && make tester 14 | ``` 15 | 16 | # 2. quality readings 17 | 18 | ### web socket 19 | - nice article https://www.bogotobogo.com/cplusplus/sockets_server_client.php 20 | - Beej's Guide to Network Programming http://beej.us/guide/bgnet/html/ 21 | - "build http server from scratch in C" https://medium.com/from-the-scratch/http-server-what-do-you-need-to-know-to-build-a-simple-http-server-from-scratch-d1ef8945e4fa 22 | - Les sockets en C https://broux.developpez.com/articles/c/sockets/ 23 | - select() http://manpages.ubuntu.com/manpages/xenial/fr/man2/select_tut.2.html 24 | - select() example https://man.developpez.com/man2/select_tut/ 25 | - select() in the details https://notes.shichao.io/unp/ch6/#readset-writeset-and-exceptset-as-value-result- 26 | - nice schema https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzab6/xnonblock.htm 27 | - select() vs threads https://www.lowtek.com/sockets/select.html 28 | - TCP 3-way handshake schema ; Blocking vs non-blocking socket https://www.bogotobogo.com/cplusplus/sockets_server_client_2.php 29 | - more socket theory to waste time https://www.bogotobogo.com/cplusplus/sockets_server_client_3.php 30 | 31 | ### http protocol 32 | - How the web works http://www.garshol.priv.no/download/text/http-tut.html 33 | - RFCs big picture, in french http://abcdrfc.online.fr/rfc-vf/pdf/rfc2616.pdf 34 | - RFCs 7230-7235 overview, in french https://www.bortzmeyer.org/http-11-reecrit.html 35 | - overview http://www.iprelax.fr/http/http_art.php 36 | - another overview https://www.commentcamarche.net/contents/520-le-protocole-http 37 | - Request and Response format https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages 38 | - another overview https://www.coozook.com/static/book-samples/B212934F6A-sample.pdf 39 | 40 | ### http headers in details 41 | - MIME list https://developer.mozilla.org/fr/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types 42 | - What HTTP response headers are required https://stackoverflow.com/questions/4726515/what-http-response-headers-are-required 43 | - response status code https://fr.wikipedia.org/wiki/Liste_des_codes_HTTP 44 | - auth theory https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Basic_authentication_scheme 45 | - auth practice https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c 46 | 47 | ### CGI programming 48 | - nice overview https://perso.liris.cnrs.fr/lionel.medini/enseignement/M1IF03/Tutoriels/Tutoriel_CGI_SSI.pdf 49 | - some details https://www.developpez.net/forums/d151285/php/langage/php-js-quoi-sert-php-cgi-exe-repertoire-php/ 50 | 51 | ### chunked encoding 52 | - wiki https://fr.wikipedia.org/wiki/Chunked_transfer_encoding 53 | - the idea https://www.geeksforgeeks.org/http-headers-transfer-encoding/ 54 | 55 | ### utils 56 | - https://superuser.com/questions/48505/how-to-find-virtual-memory-size-and-cache-size-of-a-linux-system 57 | - https://blog.seboss666.info/2015/08/reprenez-le-controle-du-cache-memoire-du-noyau-linux/ 58 | 59 | # 3. testing 60 | 61 | - python library to make http requests https://requests.readthedocs.io/en/master/ 62 | - quickly and easily send requests https://www.postman.com/ 63 | -------------------------------------------------------------------------------- /doc/bgnet_a4_bw_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/doc/bgnet_a4_bw_2.pdf -------------------------------------------------------------------------------- /doc/doc.md: -------------------------------------------------------------------------------- 1 | # syscalls to create TCP socket: 2 | 3 | --- server side 4 | - select: http://manpagesfr.free.fr/man/man2/select.2.html 5 | surveiller plusieurs descripteurs de fichier, attendant qu'au moins l'un des descripteurs de fichier devienne « prêt » 6 | (prêt = s'il est possible d'effectuer l'opération d'entrées-sorties correspondante (par exemple, un read(2)) sans bloquer) 7 | 8 | 4 macros sont disponibles pour la manipulation des ensembles 9 | FD_ZERO() efface un ensemble. 10 | FD_SET() et FD_CLR() ajoutent et suppriment, respectivement, un descripteur de fichier dans un ensemble. 11 | FD_ISSET() vérifie si un descripteur de fichier est contenu dans un ensemble, principalement utile après le retour de select(). 12 | 13 | => pour être prévenu de l'arrivée d'une connexion sur une socket, on peut utiliser select(2) ou poll(2). 14 | Un événement « lecture » sera délivré lorsqu'une tentative de connexion aura lieu, et on pourra alors appeler accept() pour la valider. 15 | 16 | RET: 17 | 18 | - socket: http://manpagesfr.free.fr/man/man2/socket.2.html 19 | crée un point de communication, et renvoie un descripteur. 20 | 21 | RET: 22 | 23 | - listen: http://manpagesfr.free.fr/man/man2/listen.2.html 24 | attendre des connexions sur une socket 25 | 26 | if (listen(server_fd, 3) < 0) 27 | { 28 | perror(“In listen”); 29 | exit(EXIT_FAILURE); 30 | } 31 | 32 | RET: 33 | 34 | - accept: http://manpagesfr.free.fr/man/man2/accept.2.html 35 | accepter une connexion sur une socket 36 | 37 | S'il n'y a pas de connexion en attente dans la file, et si la socket n'est pas marquée comme non bloquante, accept() se met en attente d'une connexion. 38 | Si la socket est non bloquante, et qu'aucune connexion n'est présente dans la file, accept() échoue avec l'erreur EAGAIN. 39 | 40 | if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) 41 | { 42 | perror("In accept"); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | RET: accept() renvoie un entier non négatif, constituant un descripteur pour la nouvelle socket 47 | (nouvelle socket = 'connected socket between a client(when you visit IP address of your server from a web browser) and a server') 48 | S'il échoue, l'appel renvoie -1 et errno contient le code d'erreur. 49 | 50 | - recv: http://manpagesfr.free.fr/man/man2/recv.2.html 51 | recevoir un message sur une socket 52 | 53 | RET: 54 | 55 | - read: 56 | 57 | char buffer[1024] = {0}; 58 | int valread = read( new_socket , buffer, 1024); 59 | printf(“%s\n”,buffer ); 60 | if(valread < 0) 61 | { 62 | printf("No bytes are there to read"); 63 | } 64 | 65 | - write: 66 | char *hello = "Hello from the server"; 67 | write(new_socket , hello , strlen(hello)); 68 | 69 | --- client side 70 | - send: 71 | envoyer un message sur une socket 72 | 73 | RET: 74 | 75 | - close: http://manpagesfr.free.fr/man/man2/close.2.html 76 | fermer un descripteur de fichier 77 | 78 | 79 | # HTTP flow 80 | 81 | When the request message reaches the server, the server can take either one of these actions: 82 | - map to file: 83 | The server interprets the request received, maps the request into a file under the server's document directory, and returns the file requested to the client. 84 | 85 | - map to program: 86 | The server interprets the request received, maps the request into a program kept in the server, executes the program, and returns the output of the program to the client. 87 | 88 | - error: 89 | The request cannot be satisfied, the server returns an error message. 90 | 91 | https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html 92 | 93 | 94 | # HTTP request methods 95 | 96 | A client can use one of these request methods to send a request message to an HTTP server. The methods are: 97 | 98 | GET request to get a web resource from the server. 99 | 100 | HEAD request to get the header that a GET request would have obtained. 101 | 102 | POST Used to post data up to the web server. 103 | 104 | PUT Ask the server to store the data. 105 | 106 | DELETE Ask the server to delete the data. 107 | 108 | TRACE Ask the server to return a diagnostic trace of the actions it takes. 109 | 110 | OPTIONS Ask the server to return the list of request methods it supports. 111 | 112 | CONNECT Used to tell a proxy to make a connection to another host and simply reply the content, without attempting to parse or cache it. 113 | 114 | ... Other extension methods. 115 | 116 | # HTTP headers to implement 117 | upply its credential (username/password) to access protected resources. 118 | 119 | ◦ Content-Language https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Language 120 | utilisé pour décrire quels langages sont destinés au public, de sorte que cela permette à l'utilisateur de se différencier en fonction de la langue préférée des utilisateurs. 121 | Par exemple, si "Content-Language: de-DE" est mis en place, cela signifie que la page est destinée à un public parlant l'allemand (par contre, cela n'indique pas que la page est écrite en allemand. Par exemple, elle pourrait être écrite en anglais dans le cadre d'un cours de langue destiné aux allemands). 122 | Si l'en-tête Content-Language n'est pas spécifié, par défaut, cela signifie que la page est destinée à tout public de langue. 123 | 124 | ◦ Content-Length number-of-bytes - Used by POST request, to inform the server the length of the request body. 125 | 126 | ◦ Content-Location "An alternate location for the returned data" 127 | Content-Location: /index.htm 128 | https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Location 129 | indique l'URL directe à utiliser pour accéder à la ressource lorsque la négociation de contenu a eu lieu, sans qu'il soit nécessaire de poursuivre la négociation de contenu. 130 | https://www.geeksforgeeks.org/http-headers-content-location/ 131 | example: Content-Location: /index.html 132 | 133 | ◦ Content-Type mime-type - Used by POST request, to inform the server the media type of the request body. 134 | 135 | ◦ Date The date and time at which the message was originated (in "HTTP-date" format as defined by RFC 7231 Date/Time Formats). 136 | 137 | ◦ Host The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening. 138 | Host: en.wikipedia.org:8080 139 | The port number may be omitted if the port is the standard port for the service requested. 140 | Host: en.wikipedia.org 141 | Mandatory since HTTP/1.1.[16] If the request is generated directly in HTTP/2, it should not be used. 142 | 143 | ◦ Last-Modified The last modified date for the requested object (in "HTTP-date" format as defined by RFC 7231) 144 | Example: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT 145 | 146 | ◦ Location Used in redirection, or when a new resource has been created. 147 | Example 1: Location: http://www.w3.org/pub/WWW/People.html 148 | Example 2: Location: /pub/WWW/People.html 149 | 150 | ◦ Referer to indicate the referrer of this request. If you click a link from web page 1 to visit web page 2, web page 1 is the referrer for request to web page 2. All major browsers set this header, which can be used to track where the request comes from 151 | 152 | ◦ Retry-After If an entity is temporarily unavailable, this instructs the client to try again later. Value could be a specified period of time (in seconds) or a HTTP-date 153 | Example 1: Retry-After: 120 154 | Example 2: Retry-After: Fri, 07 Nov 2014 23:59:59 GMT 155 | 156 | ◦ Server A name for the server 157 | 158 | ◦ Transfer-Encoding The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity. 159 | Must not be used with HTTP/2.[13] 160 | Transfer-Encoding: chunked 161 | 162 | ◦ User-Agent browser-type - Identify the type of browser used to make the request. Server use this to return different document depending on the type of browsers. 163 | https://fr.wikipedia.org/wiki/User_agent 164 | User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/12.0 165 | 166 | ◦ WWW-Authenticate 167 | 168 | # Status code 169 | 170 | https://httpstatusdogs.com/ 171 | 172 | 173 | # Pseudo code 174 | 175 | // 1 init 176 | parse_nginx_conf(); 177 | init_fds(); // FD_ZERO() + new Server() > socket() > bind() > listen() > fcntl() > FD_SET() 178 | 179 | // 2 180 | while (42): 181 | if (signal): 182 | break 183 | readSet = readSetSave 184 | writeSet = writeSetSave 185 | select() 186 | Servers.each(server): 187 | if FD_ISSET() 188 | try accept accept() > create new Client() for server ; catch error 189 | server.Clients.each(client): 190 | getRequest() > read() fd generated by accept() 191 | processResponse() selon la méthode (GET||POST||HEAD||...) 192 | clear() 193 | 194 | # NGINX.conf 195 | 196 | https://www.youtube.com/watch?v=YD_exb9aPZU&pbjreload=101 197 | 198 | -------------------------------------------------------------------------------- /intra/cgi_tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/intra/cgi_tester -------------------------------------------------------------------------------- /intra/tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/intra/tester -------------------------------------------------------------------------------- /intra/ubuntu_cgi_tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/intra/ubuntu_cgi_tester -------------------------------------------------------------------------------- /intra/ubuntu_tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/intra/ubuntu_tester -------------------------------------------------------------------------------- /project/v1/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean fclean re cache 2 | 3 | CC = clang++ 4 | CPPFLAGS = -Wall -Wextra -Werror -g 5 | VERSION = -std=c++11 6 | NAME = webserv 7 | RM = rm -rf 8 | 9 | SRC_PATH = ./srcs 10 | LIB_PATH = ./srcs/methods 11 | GNL_PATH = ./srcs/get_next_line 12 | REQ_PATH = ./srcs/request 13 | 14 | OBJ_PATH = ./objs 15 | OBJLIB_PATH = ./objs 16 | OBJGNL_PATH = ./objs 17 | OBJREQ_PATH = ./objs 18 | 19 | SRC_NAME = Conf.cpp \ 20 | main.cpp \ 21 | Server.cpp \ 22 | Client.cpp \ 23 | Logger.cpp \ 24 | Response.cpp \ 25 | CGI.cpp \ 26 | Utils.cpp \ 27 | utils_tmp.cpp \ 28 | Config_parser.cpp 29 | 30 | LIB_NAME = connect.cpp \ 31 | delete.cpp \ 32 | get.cpp \ 33 | option.cpp \ 34 | put.cpp \ 35 | trace.cpp \ 36 | post.cpp 37 | 38 | GNL_NAME = get_next_line.cpp \ 39 | get_next_line_utils.cpp 40 | 41 | 42 | REQ_NAME = Request.cpp \ 43 | Parsing.cpp \ 44 | Debug.cpp 45 | 46 | OBJ_NAME = $(SRC_NAME:.cpp=.o) 47 | OBJLIB_NAME = $(LIB_NAME:.cpp=.o) 48 | OBJGNL_NAME = $(GNL_NAME:.cpp=.o) 49 | OBJREQ_NAME = $(REQ_NAME:.cpp=.o) 50 | 51 | SRC = $(addprefix $(SRC_PATH)/, $(SRC_NAME)) 52 | LIB = $(addprefix $(LIB_PATH)/, $(LIB_NAME)) 53 | GNL = $(addprefix $(GNL_PATH)/, $(GNL_NAME)) 54 | REQ = $(addprefix $(REQ_PATH)/, $(REQ_NAME)) 55 | 56 | OBJ = $(addprefix $(OBJ_PATH)/,$(OBJ_NAME)) 57 | OBJLIB = $(addprefix $(OBJLIB_PATH)/,$(OBJLIB_NAME)) 58 | OBJGNL = $(addprefix $(OBJGNL_PATH)/,$(OBJGNL_NAME)) 59 | OBJREQ = $(addprefix $(OBJREQ_PATH)/,$(OBJREQ_NAME)) 60 | 61 | 62 | all: $(NAME) 63 | @touch ./www/perso/delete/to_delete 64 | @$(RM) ./www/tester/file_should_exist_after 65 | @$(RM) ./www/tester/multiple_same 66 | @$(RM) ./www/temp_file 67 | 68 | $(NAME): $(OBJ) $(OBJLIB) $(OBJGNL) $(OBJREQ) 69 | ${CC} ${VERSION} ${CPPFLAGS} -o ${NAME} ${OBJ} ${OBJLIB} ${OBJGNL} $(OBJREQ) 70 | 71 | $(OBJ_PATH)/%.o: $(SRC_PATH)/%.cpp 72 | @mkdir $(OBJ_PATH) 2> /dev/null || true 73 | ${CC} ${VERSION} -o $@ -c $< 74 | 75 | $(OBJLIB_PATH)/%.o: $(LIB_PATH)/%.cpp 76 | @mkdir $(OBJLIB_PATH) 2> /dev/null || true 77 | $(CC) ${VERSION} -o $@ -c $< 78 | 79 | $(OBJGNL_PATH)/%.o: $(GNL_PATH)/%.cpp 80 | @mkdir $(OBJGNL_PATH) 2> /dev/null || true 81 | $(CC) ${VERSION} -o $@ -c $< 82 | 83 | $(OBJREQ_PATH)/%.o: $(REQ_PATH)/%.cpp 84 | @mkdir $(OBJREQ_PATH) 2> /dev/null || true 85 | $(CC) ${VERSION} -o $@ -c $< 86 | 87 | clean: 88 | $(RM) $(OBJ) $(OBJLIB) $(OBJGNL) $(OBJREQ) 89 | 90 | fclean: clean 91 | $(RM) $(NAME) 92 | $(RM) ./webserv.dSYM 93 | $(RM) ./log/*.txt 94 | $(RM) ./www/tester/file_should_exist_after 95 | $(RM) ./www/tester/multiple_same 96 | $(RM) ./www/temp_file 97 | 98 | re: fclean all 99 | 100 | cache: 101 | free && sync && sudo sh -c 'echo 3 >/proc/sys/vm/drop_caches' && free 102 | 103 | launch: all 104 | ./webserv conf/tester.conf 105 | 106 | tester: 107 | ../../intra/tester http://localhost:8080 108 | 109 | ubuntu_launch: all cache 110 | ./webserv conf/ubuntu_tester.conf 111 | 112 | ubuntu_tester: cache 113 | ../../intra/ubuntu_tester http://localhost:8080 114 | 115 | valgrind: all cache 116 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./webserv conf/ubuntu_tester.conf 117 | 118 | valgrind_light: all cache 119 | valgrind ./webserv conf/ubuntu_tester.conf -------------------------------------------------------------------------------- /project/v1/commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "make fclean" 4 | make fclean 2> /dev/null || true 5 | 6 | echo 7 | echo "git add ." 8 | git add . 9 | 10 | echo 11 | echo "Enter a commit message:" 12 | MSG= read -r -p "> " msg 13 | echo "git commit -m $msg" 14 | git commit -m $msg 15 | 16 | echo 17 | echo "Push ? [Yy/Nn]:" 18 | read -p "> " -n 1 -r 19 | echo 20 | if [[ $REPLY =~ ^[Yy]$ ]] 21 | then 22 | echo "git push origin master" 23 | git push origin master 24 | fi 25 | 26 | -------------------------------------------------------------------------------- /project/v1/conf/tester.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080 3 | error ./www/tester/error 4 | name server1 5 | host localhost 6 | 7 | location / { 8 | method GET 9 | root ./www/tester 10 | index getindex.html 11 | cgi .bla 12 | cgi_path ./www/tester/CGI/cgi_tester 13 | } 14 | location /put_test { 15 | method PUT 16 | root ./www/tester 17 | } 18 | location /post_body { 19 | method POST 20 | root ./www/tester 21 | index index.html 22 | cgi .bla 23 | max_body 100 24 | } 25 | location /directory { 26 | cgi .bla 27 | method GET POST 28 | root ./www/tester/YoupiBanane 29 | index youpi.bad_extension 30 | 31 | cgi_path ./www/tester/CGI/cgi_tester 32 | } 33 | } 34 | 35 | server { 36 | listen 8081 37 | error ./www/perso/error 38 | name server1 39 | host localhost 40 | 41 | location / { 42 | root ./www/perso 43 | index index.html 44 | method GET POST HEAD OPTIONS PUT 45 | cgi_path ./www/CGI/cgi_tester 46 | } 47 | location /test { 48 | root ./www/perso/test 49 | index index.html 50 | auto_index 0 51 | method GET POST HEAD OPTIONS PUT 52 | } 53 | location /auto_index { 54 | root ./www/perso/vide 55 | index videvidevide 56 | auto_index 1 57 | method GET POST OPTIONS 58 | } 59 | location /no_auto_index { 60 | root ./www/perso/vide 61 | index videvidevide 62 | auto_index 0 63 | method GET POST HEAD OPTIONS PUT 64 | } 65 | location /delete { 66 | root ./www/perso/delete 67 | index to_delete 68 | method OPTIONS DELETE 69 | } 70 | location /put_test { 71 | method PUT 72 | root ./www/tester 73 | } 74 | location /auth { 75 | method PUT 76 | root ./www/tester 77 | auth user:pass 78 | } 79 | location /maxbody { 80 | method PUT 81 | root ./www/tester 82 | index index.html 83 | max_body 100 84 | } 85 | } 86 | 87 | server { 88 | listen 8082 89 | error ./www/perso/error 90 | name post_server 91 | host localhost 92 | 93 | location / { 94 | root ./www/methods/post 95 | index index.html 96 | method POST GET TRACE CONNECT 97 | cgi_path ./www/tester/CGI/cgi_tester 98 | cgi .cgi 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /project/v1/conf/ubuntu_tester.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080 3 | error ./www/tester/error 4 | name server1 5 | host localhost 6 | 7 | location / { 8 | method GET 9 | root ./www/tester 10 | index getindex.html 11 | cgi .bla 12 | cgi_path ./www/CGI/ubuntu_cgi_tester 13 | } 14 | location /put_test { 15 | method PUT 16 | root ./www/tester 17 | } 18 | location /post_body { 19 | method POST 20 | root ./www/tester 21 | index index.html 22 | cgi .bla 23 | max_body 100 24 | } 25 | location /directory { 26 | cgi .bla 27 | method GET POST 28 | root ./www/tester/YoupiBanane 29 | index youpi.bad_extension 30 | 31 | cgi_path ./www/CGI/ubuntu_cgi_tester 32 | } 33 | } 34 | 35 | server { 36 | listen 8081 37 | error ./www/perso/error 38 | name server2 39 | host localhost 40 | 41 | location / { 42 | root ./www/perso 43 | index index.html 44 | method GET POST HEAD OPTIONS PUT 45 | cgi_path ./www/CGI/ubuntu_cgi_tester 46 | } 47 | location /test { 48 | root ./www/perso/test 49 | index index.html 50 | auto_index 0 51 | method GET POST HEAD OPTIONS PUT 52 | } 53 | location /auto_index { 54 | root ./www/perso/vide 55 | index videvidevide 56 | auto_index 1 57 | method GET POST OPTIONS 58 | } 59 | location /no_auto_index { 60 | root ./www/perso/vide 61 | index videvidevide 62 | auto_index 0 63 | method GET POST HEAD OPTIONS PUT 64 | } 65 | location /delete { 66 | root ./www/perso/delete 67 | index to_delete 68 | method OPTIONS DELETE 69 | } 70 | location /put_test { 71 | method PUT 72 | root ./www/tester 73 | } 74 | location /auth { 75 | method PUT 76 | root ./www/tester 77 | auth user:pass 78 | } 79 | location /maxbody { 80 | method PUT 81 | root ./www/tester 82 | index index.html 83 | max_body 100 84 | } 85 | } 86 | 87 | server { 88 | listen 8082 89 | error ./www/perso/error 90 | name post_server 91 | host localhost 92 | 93 | location / { 94 | root ./www/methods/post 95 | index index.html 96 | method POST GET TRACE CONNECT 97 | cgi_path ./www/CGI/ubuntu_cgi_tester 98 | cgi .cgi 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /project/v1/includes/Client.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CLIENT_HPP 2 | #define CLIENT_HPP 3 | 4 | /* 5 | ** Libraries 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | ** Headers 16 | */ 17 | 18 | #include "Request.hpp" 19 | #include "Response.hpp" 20 | class Server; 21 | 22 | /* 23 | ** Const 24 | */ 25 | 26 | # define RECV_BUFFER 65536 27 | 28 | /* 29 | ** Class 30 | */ 31 | 32 | class Client 33 | { 34 | /* 35 | ** member variables 36 | */ 37 | 38 | private: 39 | // 40 | 41 | protected: 42 | // 43 | 44 | public: 45 | Server *_server; 46 | int _accept_fd; 47 | std::string _ip; 48 | int _port; 49 | 50 | Request _request; 51 | Response _response; 52 | 53 | bool _is_finished; 54 | bool _is_connected; 55 | 56 | char *_buffermalloc; 57 | std::string _concat_body; 58 | int _line_size; 59 | 60 | std::string _last_active_time; 61 | int _wfd; 62 | int _rfd; 63 | int _pid; 64 | int _read_ok; 65 | 66 | enum status 67 | { 68 | HEADER, 69 | BODY, 70 | COMPLETE, 71 | WAITING, 72 | ERROR 73 | }; 74 | 75 | int recv_status; 76 | 77 | /* 78 | ** methods 79 | */ 80 | 81 | private: 82 | Client(); 83 | 84 | protected: 85 | // 86 | 87 | public: 88 | Client(Server *server, int accept_fd, struct sockaddr_in addr); 89 | ~Client(); 90 | 91 | void reset(void); 92 | void write_file(); 93 | void read_file(std::string &buff); 94 | 95 | /* 96 | ** friends 97 | */ 98 | 99 | friend class Server; 100 | friend class Request; 101 | friend class Response; 102 | }; 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /project/v1/includes/Code.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CODE_HPP 2 | #define CODE_HPP 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | ** Const 9 | */ 10 | 11 | #define OK_200 200 12 | #define CREATED_201 201 13 | #define ACCEPTED_202 202 14 | #define NO_CONTENT_204 204 15 | #define BAD_REQUEST_400 400 16 | #define UNAUTHORIZED_401 401 17 | #define NOT_FOUND_404 404 18 | #define METHOD_NOT_ALLOWED_405 405 19 | #define REQUEST_ENTITY_TOO_LARGE_413 413 20 | #define SERVICE_UNAVAILABLE_503 503 21 | #define NOT_IMPLEMENTED_501 501 22 | #define INTERNAL_ERROR_500 500 23 | 24 | /* 25 | ** status_code => reason_phrase 26 | */ 27 | 28 | static std::map code_to_reason = { 29 | 30 | { 100, "Continue" }, 31 | { 101, "Switching Protocols" }, 32 | { 102, "Processing" }, 33 | { 103, "Early hints" }, 34 | 35 | { 200, "OK" }, 36 | { 201, "Created" }, 37 | { 202, "Accepted" }, 38 | { 203, "Non-Authoritative Information" }, 39 | { 204, "No Content" }, 40 | { 205, "Reset Content" }, 41 | { 206, "Partial Content" }, 42 | { 207, "Multi-Status" }, 43 | { 208, "Already Reported" }, 44 | { 210, "Content Different" }, 45 | { 226, "IM Used" }, 46 | 47 | { 300, "Multiple Choices" }, 48 | { 301, "Moved permanently" }, 49 | { 302, "Found" }, 50 | { 303, "See Other" }, 51 | { 304, "Not Modified" }, 52 | { 305, "Use Proxy" }, 53 | { 306, "Switch Proxy" }, 54 | { 307, "Temporary Redirect" }, 55 | { 308, "Permanent Redirect" }, 56 | { 310, "Too Many Redirects" }, 57 | 58 | { 400, "Bad Request" }, 59 | { 401, "Unauthorized" }, 60 | { 402, "Payment Required" }, 61 | { 403, "Forbidden" }, 62 | { 404, "Not Found" }, 63 | { 405, "Method Not Allowed" }, 64 | { 406, "Not Acceptable" }, 65 | { 407, "Proxy Authentication Required" }, 66 | { 408, "Request Time-Out" }, 67 | { 409, "Conflict" }, 68 | { 410, "Gone" }, 69 | { 411, "Length Required" }, 70 | { 412, "Precondition Failed" }, 71 | { 413, "Request Entity Too Large" }, 72 | { 414, "Request-URI Too Long" }, 73 | { 415, "Unsupported Media Type" }, 74 | { 416, "Requested range unsatisfiable" }, 75 | { 417, "Expectation failed" }, 76 | { 418, "I'm a teapot" }, 77 | { 421, "Bad mapping" }, 78 | { 422, "Unprocessable entity" }, 79 | { 423, "Locked" }, 80 | { 424, "Method failure" }, 81 | { 425, "Unordered Collection" }, 82 | { 426, "Upgrade Required" }, 83 | { 428, "Precondition Required" }, 84 | { 429, "Too Many Requests" }, 85 | { 431, "Request Header Fileds Too Large" }, 86 | { 449, "Retry With" }, 87 | { 450, "Blocked by Windows Parental Control" }, 88 | { 451, "Unavailable for Legal Reasons" }, 89 | { 456, "Unrecoverable Error" }, 90 | 91 | { 444, "No Response" }, 92 | { 495, "SSL Certificate Error" }, 93 | { 496, "SSL Certificate Required" }, 94 | { 497, "HTTP Request Sent to HTTPS Port" }, 95 | { 498, "Token Expired/Invalid" }, 96 | { 499, "Client Closed Request" }, 97 | 98 | { 500, "Internal Server Error" }, 99 | { 501, "Not Implemented" }, 100 | { 502, "Bad Gateway" }, 101 | { 503, "Service Unavailable" }, 102 | { 504, "Gateway Time-Out" }, 103 | { 505, "HTTP Version non supported" }, 104 | { 506, "Variant Also Negotiates" }, 105 | { 507, "Insufficient storage" }, 106 | { 508, "Loop Detected" }, 107 | { 509, "Bandwidth Limit Exceeded" }, 108 | { 510, "Not extended" }, 109 | { 511, "Network authentication required" }, 110 | 111 | { 520, "Unknown Error" }, 112 | { 521, "Web Server Is Down" }, 113 | { 522, "Connection Timed Out" }, 114 | { 523, "Origin is Unreachable" }, 115 | { 524, "A Timeout Occurred" }, 116 | { 525, "SSL Handshake Failed" }, 117 | { 526, "Invalid SSL Certificate" }, 118 | { 527, "Railgun Error" } 119 | 120 | }; 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /project/v1/includes/Colors.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLORS_HPP 2 | # define COLORS_HPP 3 | 4 | // https://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal 5 | 6 | # define BLACK_C "\033[1;30m" 7 | # define RED_C "\033[1;31m" 8 | # define GREEN_C "\033[1;32m" 9 | # define YELLOW_C "\033[1;33m" 10 | # define BLUE_C "\033[1;34m" 11 | # define MAGENTA_C "\033[1;35m" 12 | # define CYAN_C "\033[1;36m" 13 | # define WHITE_C "\033[1;37m" 14 | 15 | # define BLACK_B "\033[1;40m" 16 | # define RED_B "\033[1;41m" 17 | # define GREEN_B "\033[1;42m" 18 | # define YELLOW_B "\033[1;43m" 19 | # define BLUE_B "\033[1;44m" 20 | # define MAGENTA_B "\033[1;45m" 21 | # define CYAN_B "\033[1;46m" 22 | # define WHITE_B "\033[1;47m" 23 | 24 | # define RESET "\033[1;0m" 25 | # define BOLD "\033[1;1m" 26 | # define BOLD_OFF "\033[1;21m" 27 | # define UNDERLINE "\033[1;4m" 28 | # define UNDERLINE_OFF "\033[1;24m" 29 | # define INVERSE "\033[1;7m" 30 | # define INVERSE_OFF "\033[1;27m" 31 | 32 | #endif -------------------------------------------------------------------------------- /project/v1/includes/Conf.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONF_HPP 2 | #define CONF_HPP 3 | 4 | /* 5 | ** Const 6 | */ 7 | 8 | #define CLIENT_CONNECTION_TIMEOUT 60 9 | #define OPEN_MAX_PADDING 10 10 | 11 | /* 12 | ** Libraries 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* 24 | ** Headers 25 | */ 26 | 27 | #include "Server.hpp" 28 | #include "Config_parser.hpp" 29 | 30 | class Server; 31 | 32 | /* 33 | ** Class 34 | */ 35 | 36 | class Conf 37 | { 38 | /* 39 | ** member variables 40 | */ 41 | 42 | private: 43 | // 44 | 45 | protected: 46 | // 47 | 48 | public: 49 | bool _on; 50 | std::string _webserv; 51 | 52 | // select() related 53 | int _nfds; 54 | fd_set _readfds; 55 | fd_set _save_readfds; 56 | fd_set _writefds; 57 | fd_set _save_writefds; 58 | fd_set _exceptfds; 59 | fd_set _save_exceptfds; 60 | struct timeval _timeout; 61 | 62 | // other variables 63 | std::vector _servers; 64 | std::list _active_fds; 65 | 66 | // debug 67 | int _nb_accepted_connections; 68 | 69 | /* 70 | ** methods 71 | */ 72 | 73 | private: 74 | void loop_fd_set() const; 75 | 76 | protected: 77 | // 78 | 79 | public: 80 | Conf(); 81 | 82 | int parse(char *conf); 83 | void reset_fd_sets(void); 84 | int get_nfds(void) const; 85 | int get_nb_open_fds(void) const; 86 | 87 | void add_fd(int fd); 88 | void remove_fd(int fd); 89 | 90 | int run_select(); 91 | }; 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /project/v1/includes/Config_parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_PARSER_HPP 2 | # define CONFIG_PARSER_HPP 3 | 4 | # include 5 | # include 6 | # include 7 | # include 8 | 9 | # include "Server.hpp" 10 | # include "Logger.hpp" 11 | # include "utils_tmp.hpp" 12 | # include "../srcs/get_next_line/get_next_line.hpp" 13 | 14 | # define WHITE_SPACE "\t\n\v\f\r " 15 | 16 | // Server tokens 17 | # define _HOST "host" 18 | # define _NAME "name" 19 | # define _PORT "listen" 20 | # define _ERROR_PAGE "error" 21 | 22 | // Location tokens 23 | # define _METHOD "method" 24 | # define _ROOT "root" 25 | # define _INDEX "index" 26 | # define _CGI_PATH "cgi_path" 27 | # define _PHP_PATH "php_path" 28 | # define _CGI "cgi" 29 | # define _AUTO_INDEX "auto_index" 30 | # define _MAX_BODY "max_body" 31 | # define _AUTH "auth" 32 | 33 | class Conf; 34 | 35 | extern Conf g_conf; 36 | 37 | typedef struct s_loc 38 | { 39 | std::string uri; 40 | std::vector method; 41 | std::string root; 42 | std::string index; 43 | std::string cgi_path; 44 | std::string php_path; 45 | std::string cgi; 46 | int auto_index; 47 | int max_body; 48 | std::string auth; 49 | s_loc() { 50 | auto_index = -1; 51 | max_body = -1; 52 | } 53 | } t_loc; 54 | 55 | typedef struct s_serv 56 | { 57 | std::string host; 58 | std::string name; 59 | std::string port; 60 | std::string error_page; 61 | std::vector loc; 62 | } t_serv; 63 | 64 | class Config_parser 65 | { 66 | private: 67 | char *conf; 68 | int fd; 69 | int line_count; 70 | std::vector serv; 71 | 72 | public: 73 | Config_parser(char *conf); 74 | ~Config_parser(); 75 | void setup_server(); 76 | 77 | private: 78 | void parse_conf(); 79 | void parse_server(); 80 | void parse_location(std::vector &token, t_serv &serv); 81 | void check_conf(); 82 | 83 | void add_serv_values(std::vector &tokens, t_serv &serv); 84 | void add_loc_values(std::vector &tokens, t_loc&loc); 85 | 86 | void fail(const std::string &message); 87 | void fail_double_token(std::string &str); 88 | void fail_double_token(int val); 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /project/v1/includes/Headers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HEADERS_HPP 2 | #define HEADERS_HPP 3 | 4 | /* 5 | ** Const 6 | */ 7 | 8 | #define RET_ERROR 0 9 | #define RET_SUCCESS 1 10 | 11 | #define EXIT_ERROR 1 12 | #define EXIT_SUCCESS 0 13 | 14 | #ifdef __linux__ 15 | # define OPEN_MAX FOPEN_MAX 16 | #endif 17 | 18 | /* 19 | ** Libraries 20 | */ 21 | 22 | #include 23 | 24 | /* 25 | ** Other headers 26 | */ 27 | 28 | #include "Conf.hpp" 29 | #include "Logger.hpp" 30 | #include "Colors.hpp" 31 | 32 | // https://stackoverflow.com/questions/3627941/global-variable-within-multiple-files 33 | extern Conf g_conf; 34 | void print_clients_of_all_servers(void); 35 | void print_all_fd(); 36 | 37 | /* 38 | ** Debug 39 | */ 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /project/v1/includes/Location.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOCATION_HPP 2 | #define LOCATION_HPP 3 | 4 | #include "Conf.hpp" 5 | 6 | class Location 7 | { 8 | private: 9 | Location(); 10 | 11 | public: 12 | 13 | std::string _uri; 14 | std::string _root; 15 | std::string _index; 16 | std::vector _method; 17 | std::string _cgi_root; 18 | std::string _php_root; 19 | std::string _cgi; 20 | int _autoindex; 21 | int _max_body; 22 | std::string _auth; 23 | 24 | Location(std::string uri, std::string root, std::string index, std::vector method, 25 | std::string cgi_root, std::string php_root, std::string cgi, int autoindex, int max_body, 26 | std::string auth) 27 | { 28 | _uri = uri; 29 | _root = root; 30 | _index = index; 31 | _method = method; 32 | _cgi_root = cgi_root; 33 | _php_root = php_root; 34 | _cgi = cgi; 35 | _autoindex = autoindex; 36 | _max_body = max_body; 37 | _auth = auth; 38 | }; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /project/v1/includes/Logger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_HPP 2 | # define LOGGER_HPP 3 | 4 | # include 5 | # include 6 | # include 7 | # include 8 | 9 | # include "utils_tmp.hpp" 10 | 11 | # define LOG_START(MIN_PRIORITY, FILE, DATE) Logger::Start(MIN_PRIORITY, FILE, DATE); 12 | # define LOG_STOP() Logger::Stop(); 13 | # define LOG_WRT(PRIORITY, MESSAGE) Logger::Write(PRIORITY, MESSAGE); 14 | # define ERROR_RET(MESSAGE) Logger::Error(MESSAGE) 15 | 16 | class Logger 17 | { 18 | public: 19 | enum Priority 20 | { 21 | DEBUG, 22 | INFO, 23 | ERROR, 24 | CLEAR 25 | }; 26 | 27 | static void Start(Priority minPriority, const std::string &logFile, bool date); 28 | static void Stop(); 29 | static void Write(Priority priority, const std::string &message); 30 | static int Error(const std::string &message); 31 | static void ChangeFile(void); 32 | ~Logger(); 33 | 34 | private: 35 | Logger(); 36 | Logger(const Logger &the_Logger) = delete; 37 | Logger &operator=(const Logger &the_Logger) = delete; 38 | 39 | bool active; 40 | bool isdate; 41 | std::ofstream fileStream; 42 | Priority minPriority; 43 | static const std::string PRIORITY_NAMES[]; 44 | static Logger instance; 45 | 46 | std::string file; 47 | int request; 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /project/v1/includes/Request.hpp: -------------------------------------------------------------------------------- 1 | #ifndef REQUEST_HPP 2 | #define REQUEST_HPP 3 | 4 | /* 5 | ** Doc 6 | */ 7 | 8 | // https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html 9 | // see 'HTTP Request and Response Messages ' 10 | // https://developer.mozilla.org/fr/docs/Web/HTTP/Headers 11 | 12 | /* 13 | ** Libraries 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | /* 24 | ** Headers 25 | */ 26 | 27 | #include "Server.hpp" 28 | #include "Location.hpp" 29 | class Client; 30 | 31 | /* 32 | ** Const 33 | */ 34 | 35 | # define CHUNKED 0 36 | # define APPLICATION 1 37 | # define FORM 2 38 | # define TEXT 3 39 | 40 | /* 41 | ** Class 42 | */ 43 | 44 | class Request 45 | { 46 | /* 47 | ** member variables 48 | */ 49 | 50 | private: 51 | // 52 | 53 | protected: 54 | // 55 | 56 | public: 57 | Client *_client; 58 | Location *_location; 59 | std::string _buffer; 60 | std::string _file; 61 | std::string _file_name; 62 | int _body_length; 63 | int _saved_error; // saving an error status code while keep going reading the end of the request, to allow the select() to switch on the right to write on _accept_fd 64 | 65 | /* 66 | ** Request Line 67 | */ 68 | 69 | std::string _method; // 1 70 | std::string _uri; // 2 71 | std::string _http_version; // 3 72 | 73 | std::string _query; //c'et quoi ? 74 | 75 | std::string _body_file; 76 | bool _is_body_file_header; 77 | /* 78 | ** Request Headers, dans l'ordre du sujet 79 | */ 80 | 81 | // - Négociation de contenu: 82 | std::map _accept_charset; // 4 indique le jeu de caractères que le client est capable de comprendre. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Accept-Charset 83 | // { 1: "utf-8", 2: "iso-8859-1;q=0.5" } 84 | 85 | std::map _accept_language; // 5 indique quelles sont les langues que le client est capable de comprendre, et quelle variante locale est préférée https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Accept-Language 86 | // - Authentification: 87 | std::string _authorization; // 6 contient les identifiants permettant l'authentification d'un utilisateur https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Authorization 88 | // - informations sur le corps du message: 89 | std::map _content_language; // 7 pour décrire quels langages sont destinés au public, de sorte que cela permette à l'utilisateur de se différencier en fonction de la langue préférée des utilisateurs. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Language 90 | int _content_length; // 8 indique la taille en octets (exprimée en base 10) du corps de la réponse envoyée au client. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Length 91 | std::string _content_location; // 9 indicates an alternate location for the returned data. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Location 92 | std::string _content_type; // 10 sert à indiquer le type MIME de la ressource. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Type 93 | // - Autres: 94 | std::string _date; // 11 la date et l'heure d'origine du message. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Date 95 | // - Contexte de requête: 96 | std::string _host; // 12 spécifie le nom de domaine du serveur https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Host 97 | std::string _referer; // 13 l'adresse de la page web précédente à partir de laquelle un lien a été suivi pour demander la page courante. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Referer 98 | std::string _user_agent; // 14 string that lets servers and network peers identify the application, operating system, vendor, and/or version of the requesting user agent. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/User-Agent 99 | 100 | std::string _secret_header; 101 | 102 | /* 103 | ** Request body 104 | */ 105 | 106 | int _body_type; 107 | std::string _text_body; 108 | 109 | /* 110 | ** // Other headers not mentionned in the subject 111 | ** 112 | */ 113 | 114 | std::string _transfer_encoding; // 17 specifies the form of encoding used to safely transfer the payload body to the user. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Transfer-Encoding 115 | std::string _keep_alive; // 18 allows the sender to hint about how the connection may be used to set a timeout and a maximum amount of requests. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive 116 | 117 | /* 118 | ** methods 119 | */ 120 | 121 | private: 122 | void fill_request(std::string key, std::string value); 123 | 124 | protected: 125 | // 126 | 127 | public: 128 | // Request.cpp 129 | Request(); 130 | void init(); 131 | std::map headers_to_map(void); 132 | void reset(); 133 | 134 | // Parsing.cpp 135 | int parse_request_line(void); 136 | int parse_headers(void); 137 | void create_autoindex(); 138 | int get_location(std::string *uri, std::vector location); 139 | int parse_filename(std::vector location); 140 | int parse(std::vector location); 141 | 142 | void parse_body_length(); 143 | void parse_body_chunked(); 144 | void update_body(); 145 | 146 | void parse_query_string(); 147 | 148 | // Debug.cpp 149 | void display(void); 150 | 151 | /* 152 | ** friends 153 | */ 154 | 155 | friend class Client; 156 | }; 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /project/v1/includes/Response.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RESPONSE_HPP 2 | #define RESPONSE_HPP 3 | 4 | /* 5 | ** Doc 6 | */ 7 | 8 | // https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html 9 | // see 'HTTP Request and Response Messages ' 10 | // https://developer.mozilla.org/fr/docs/Web/HTTP/Headers 11 | 12 | /* 13 | ** Libraries 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* 28 | ** Headers 29 | */ 30 | 31 | #include "Utils.hpp" 32 | #include "Request.hpp" 33 | #include "Code.hpp" 34 | 35 | class Client; 36 | 37 | /* 38 | ** Class 39 | */ 40 | 41 | class Response 42 | { 43 | /* 44 | ** member variables 45 | */ 46 | 47 | private: 48 | // 49 | 50 | protected: 51 | // 52 | 53 | public: 54 | 55 | Client *_client; 56 | 57 | /* 58 | ** Status Line 59 | */ 60 | 61 | std::string _http_version; 62 | int _status_code; 63 | std::string _reason_phrase; 64 | 65 | /* 66 | ** Response Headers, dans l'ordre du sujet 67 | */ 68 | 69 | // - Contexte de réponse 70 | std::string _allow; // liste les méthodes supportées par une ressource. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Allow 71 | // - Informations sur le corps du message: 72 | std::map _content_language; // pour décrire quels langages sont destinés au public, de sorte que cela permette à l'utilisateur de se différencier en fonction de la langue préférée des utilisateurs. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Language 73 | int _content_length; // indique la taille en octets (exprimée en base 10) du corps de la réponse envoyée au client. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Length 74 | std::map _content_location; // indicates an alternate location for the returned data. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Location 75 | std::map _content_type; // sert à indiquer le type MIME de la ressource. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Type 76 | // - Conditionnels: 77 | std::string _last_modified; // la date et l'heure à laquelle le serveur d'origine pense que la ressource a été modifiée pour la dernière fois. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Last-Modified 78 | // - Redirection: 79 | std::string _location; // indique l'URL vers laquelle rediriger une page. Il a un sens seulement lorsqu'il est servi avec une réponse d'état 3xx (redirection) ou 201 (créé). https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Location 80 | // - Autres: 81 | std::string _date; // la date et l'heure d'origine du message. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Date 82 | int _retry_after; // indicates how long the user agent should wait before making a follow-up request. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Retry-After 83 | // - Contexte de réponse: 84 | std::string _server; // contient des informations à propos du système (ou sous-système) en place sur le serveur qui s'occupe de la requête. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Serveur 85 | // - Contexte de réponse: 86 | std::string _transfer_encoding; // specifies the form of encoding used to safely transfer the payload body to the user. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Transfer-Encoding 87 | // - Authentification: 88 | std::map _www_authenticate; // définit la méthode d'authentification qui doit être utilisé pour obtenir l'accès à une ressource. https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/WWW-Authenticate 89 | 90 | /* 91 | ** Response body 92 | */ 93 | 94 | std::string _body; 95 | 96 | /* 97 | ** Response body 98 | */ 99 | 100 | std::string _to_send; 101 | 102 | /* 103 | ** Other headers (not mentionned in the subject but useful to consider) 104 | */ 105 | 106 | std::string _keep_alive; // 18 allows the sender to hint about how the connection may be used to set a timeout and a maximum amount of requests. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive 107 | 108 | /* 109 | ** Other 110 | */ 111 | 112 | size_t _bytes_send; 113 | 114 | int read_fd; 115 | enum status 116 | { 117 | HANDLE_RESPONSE, 118 | SENDING, 119 | COMPLETE 120 | }; 121 | int send_status; 122 | 123 | /* 124 | ** methods 125 | */ 126 | 127 | private: 128 | void init(void); 129 | void reset(void); 130 | int concat_to_send(void); 131 | 132 | protected: 133 | // 134 | 135 | public: 136 | Response(void); 137 | 138 | int format_to_send(Request *req); 139 | 140 | void handle_response(Request *req); 141 | 142 | void get(Request *req); 143 | void post(Request *req); 144 | void put(Request *req); 145 | void ft_delete(Request *req); 146 | void option(Request *req); 147 | void trace(Request *req); 148 | void connect(Request *req); 149 | 150 | int bad_request(Request *req); 151 | int accepted_method(Request *req); 152 | int method_not_allowed(Request *req); 153 | int unauthorized(Request *req); 154 | int request_entity_too_large(Request *req); 155 | int not_found(Request *req); 156 | int service_unavailable(Request *req); 157 | 158 | char **create_env_tab(Request *req); 159 | void ft_cgi(Request *req); 160 | void get_cgi_ret(Request *req); 161 | 162 | int build_chunked(Request &req, char *buffer, int ret); 163 | /* 164 | ** friends 165 | */ 166 | 167 | friend class Client; 168 | }; 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /project/v1/includes/Server.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_HPP 2 | #define SERVER_HPP 3 | 4 | /* 5 | ** Const 6 | */ 7 | 8 | #define UNAVAILABLE_TIME 20 9 | 10 | /* 11 | ** Libraries 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include // pour les familles de protocoles (AF_INET, etc.) 18 | #include 19 | #include // sockaddr_in 20 | #include 21 | #include // fcntl - Manipuler un descripteur de fichier http://manpagesfr.free.fr/man/man2/fcntl.2.html 22 | #include // select, fd_set 23 | 24 | /* 25 | ** Headers 26 | */ 27 | 28 | #include "Client.hpp" 29 | // #include "Request.hpp" 30 | #include "Location.hpp" 31 | 32 | /* 33 | ** Class 34 | */ 35 | 36 | class Server 37 | { 38 | /* 39 | ** member variables 40 | */ 41 | 42 | private: 43 | 44 | protected: 45 | // 46 | 47 | public: 48 | std::string _host; 49 | std::string _name; 50 | int _port; 51 | std::string _error; 52 | int _socket_fd; 53 | struct sockaddr_in _addr; 54 | 55 | std::vector _locations; 56 | std::vector _clients; 57 | std::vector _clients_503; 58 | 59 | /* 60 | ** methods 61 | */ 62 | 63 | private: 64 | Server(); 65 | 66 | protected: 67 | // 68 | 69 | public: 70 | Server(std::string serverName, int port, std::string host, std::string error_page); 71 | ~Server(); 72 | 73 | int start(void); 74 | 75 | int acceptNewClient(void); 76 | 77 | int recvRequest(Client *c); 78 | int sendResponse(Client *c); 79 | int handleClientRequest(Client *c); 80 | 81 | /* 82 | ** friends 83 | */ 84 | 85 | friend class Conf; 86 | }; 87 | 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /project/v1/includes/Utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_HPP 2 | #define UTILS_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "Request.hpp" 12 | 13 | std::string get_last_modif(std::string file); 14 | std::string get_date(void); 15 | std::string get_content_type(std::string file); 16 | std::string get_location_header(Request *req); 17 | std::string map_to_string(std::map map, char delim); 18 | std::string vector_to_string(std::vector map, char delim); 19 | void displayMap(std::map map); 20 | int is_extension(std::string file, std::string ext); 21 | 22 | int set_laguage(Request *req); 23 | int set_charset(Request *req); 24 | void unset_extension(Request *req); 25 | int compare_date(std::string a, std::string b); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /project/v1/includes/utils_tmp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_TMP_HPP 2 | # define UTILS_TMP_HPP 3 | 4 | # include 5 | # include 6 | # include 7 | # include 8 | # include 9 | # include 10 | # include 11 | # include 12 | # include 13 | # include 14 | # include "../srcs/get_next_line/get_next_line.hpp" 15 | # include "Logger.hpp" 16 | # include 17 | 18 | 19 | class utils_tmp 20 | { 21 | public: 22 | static std::string get_date(void); 23 | static int isspace(int c); 24 | static std::vector split_string(std::string &str, std::string set); 25 | static bool file_exists(const char *filename); 26 | static int get_buffer(std::string file, std::string &buff); 27 | static int extract_body(std::string &buff); 28 | static int hexa_to_dec(const char *hexVal); 29 | static void remove_return(std::string &str); 30 | static size_t getSecondsDiff(std::string complete_time); 31 | static void ft_getline(std::string &b, std::string &line); 32 | static std::vector split(const std::string& str, char delim); 33 | static std::string trim(const std::string& str); 34 | static void print_map(std::stringstream &ss1, std::map map); 35 | static void free_strtab(char ***tab); 36 | static int is_valide_methods(std::string &meth); 37 | static std::string dec_to_hex(long int dec); 38 | static size_t find_body_position(std::string file); 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /project/v1/other_tests/siege.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./webserv ./conf/tester.conf & 4 | siege --quiet -bt2s http://localhost:8080/ &>/dev/null & -------------------------------------------------------------------------------- /project/v1/other_tests/stress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | i=0; 4 | while (($i < 300)) ;do 5 | curl -H "Keep-Alive: 60" -H "Connection: keep-alive" http://localhost:8080; 6 | i=$(($i+1)); 7 | done 8 | 9 | # curl -X POST -H "Content-Type: plain/test" --data "BODY IS HERE write something shorter or longer than body limit BODY IS HERE write something shorter or longer than body limit BODY IS HERE write something shorter or longer than body limit " http://localhost:8080/post_body; -------------------------------------------------------------------------------- /project/v1/srcs/CGI.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Headers.hpp" 2 | 3 | /* 4 | ** doc: 5 | ** https://perso.liris.cnrs.fr/lionel.medini/enseignement/M1IF03/Tutoriels/Tutoriel_CGI_SSI.pdf 6 | */ 7 | 8 | /* 9 | ** pseudo code create_env_tab: 10 | ** 1 créer une map et ajouter toutes les variables nécessaires aux cgi cf. doc 11 | ** 2 ajouter les headers de la requêtes avec préfixe 'HTTP_' 12 | ** 3 convertir la map en un char **env à passer en 3ème argument de execve 13 | */ 14 | 15 | char **Response::create_env_tab(Request *req) 16 | { 17 | char **args_to_tab; 18 | std::map args_to_map; 19 | std::map headers; 20 | size_t pos; 21 | 22 | // 1 23 | args_to_map["GATEWAY_INTERFACE"] = "CGI/1.1"; // version des spécifications CGI utilisées par le serveur 24 | args_to_map["SERVER_PROTOCOL"] = "HTTP/1.1"; // protocole et version de la requête en cours de traitement 25 | args_to_map["SERVER_SOFTWARE"] = g_conf._webserv; // nom et version du démon HTTP 26 | args_to_map["REQUEST_URI"] = req->_uri; 27 | args_to_map["REQUEST_METHOD"] = req->_method; // méthode associée à la requête en cours de traitement 28 | args_to_map["REMOTE_ADDR"] = req->_client->_ip; // adresse IP de la machine d'où vient la requête 29 | args_to_map["PATH_INFO"] = req->_uri; // chaîne entre SCRIPT_PATH et QUERY_STRING dans l'URL 30 | args_to_map["CONTENT_LENGTH"] = std::to_string(req->_text_body.length()); // longueur des données véhiculées dans la requête (POST) 31 | if (!req->_query.empty()) 32 | args_to_map["QUERY_STRING"] = req->_query; // données transmises au CGI via l'URL (GET) 33 | else 34 | args_to_map["QUERY_STRING"]; // données transmises au CGI via l'URL (GET) 35 | args_to_map["CONTENT_TYPE"] = req->_content_type; // type MIME des données véhiculées dans la requête 36 | args_to_map["SCRIPT_NAME"] = req->_location->_cgi_root; // chemin du CGI à partir de la racine du serveur HTTP 37 | args_to_map["SERVER_NAME"] = req->_client->_server->_name; // nom ou adresse IP de la machine serveur HTTP 38 | args_to_map["SERVER_PORT"] = std::to_string(req->_client->_server->_port); // numéro du port (TCP) vers lequel la requête a été envoyée 39 | if (!req->_authorization.empty()) 40 | { 41 | pos = req->_authorization.find(" "); 42 | args_to_map["AUTH_TYPE"] = req->_authorization.substr(0, pos); // méthode d'authentification de l'utilisateur s'il y a lieu 43 | args_to_map["REMOTE_USER"] = req->_authorization.substr(pos + 1); // si authentification, nom de l'utilisateur associé à la requête 44 | args_to_map["REMOTE_IDENT"] = req->_authorization.substr(pos + 1); // login de connexion de l'utilisateur (pas souvent supporté) 45 | } 46 | headers = req->headers_to_map(); 47 | std::map::iterator it = headers.begin(); 48 | while (it != headers.end()) 49 | { 50 | if (!it->second.empty()) 51 | args_to_map["HTTP_" + it->first] = it->second; // une variable pour chaque champ contenu dans l'en-tête HTTP 52 | ++it; 53 | } 54 | args_to_tab = (char **)malloc(sizeof(char *) * (args_to_map.size() + 1)); 55 | it = args_to_map.begin(); 56 | int i = 0; 57 | while (it != args_to_map.end()) 58 | { 59 | LOG_WRT(Logger::DEBUG, it->first + " = " + it->second); 60 | args_to_tab[i++] = strdup((it->first + "=" + it->second).c_str()); 61 | ++it; 62 | } 63 | args_to_tab[i] = NULL; 64 | return (args_to_tab); 65 | } 66 | 67 | void Response::ft_cgi(Request *req) 68 | { 69 | char **env; 70 | char **args; 71 | int tubes[2]; 72 | int ret; 73 | int ret2; 74 | int temp_fd; 75 | pid_t pid; 76 | struct stat php; 77 | std::string binaire; 78 | int status; 79 | 80 | if (req->_location->_cgi_root != "") 81 | binaire = req->_location->_cgi_root; 82 | else 83 | binaire = req->_location->_php_root; 84 | env = create_env_tab(req); 85 | args = (char **)(malloc(sizeof(char *) * 3)); 86 | if (req->_location->_cgi_root != "") 87 | args[0] = strdup(req->_location->_cgi_root.c_str()); 88 | else 89 | args[0] = strdup(req->_location->_php_root.c_str()); 90 | args[1] = strdup(req->_file.c_str()); 91 | args[2] = NULL; 92 | temp_fd = open("./www/temp_file", O_WRONLY | O_CREAT, 0666); 93 | pipe(tubes); 94 | 95 | if (req->_method == "GET") 96 | close(tubes[1]); 97 | if ((req->_client->_pid = fork()) == 0) 98 | { 99 | dup2(temp_fd, 1); 100 | if (stat(binaire.c_str(), &php) != 0 || 101 | !(php.st_mode & S_IFREG)) 102 | { 103 | std::cout << "error CGI\n"; 104 | exit(1); 105 | } 106 | dup2(tubes[0], 0); 107 | errno = 0; 108 | if ((ret = execve(binaire.c_str(), args, env)) == -1) 109 | { 110 | std::cout << "error execve: " << std::string(strerror(errno)) << std::endl; 111 | exit(1); 112 | } 113 | } 114 | else 115 | { 116 | if (req->_method == "POST") 117 | { 118 | close(tubes[0]); 119 | req->_client->_wfd = tubes[1]; 120 | FD_SET(req->_client->_wfd, &g_conf._save_writefds); 121 | g_conf.add_fd(req->_client->_wfd); 122 | } 123 | utils_tmp::free_strtab(&args); 124 | utils_tmp::free_strtab(&env); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /project/v1/srcs/Client.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Headers.hpp" 2 | 3 | /* 4 | ** constructors / destructors 5 | */ 6 | 7 | Client::Client(Server *server, int accept_fd, struct sockaddr_in addr): 8 | _server(server), _accept_fd(accept_fd), _is_connected(true) 9 | { 10 | // convertit l'adresse Internet de l'hôte cp depuis la notation IPv4 décimale pointée 11 | // vers une forme binaire (dans l'ordre d'octet du réseau), 12 | // et la stocke dans la structure pointée 13 | // http://manpagesfr.free.fr/man/man3/inet.3.html 14 | _ip = inet_ntoa(addr.sin_addr); 15 | _port = htons(addr.sin_port); 16 | _request._client = this; 17 | _response._client = this; 18 | FD_SET(_accept_fd, &g_conf._save_readfds); 19 | g_conf.add_fd(_accept_fd); 20 | _buffermalloc = (char *)malloc(sizeof(char) * (RECV_BUFFER + 1)); 21 | memset((void *)_buffermalloc, '\0', RECV_BUFFER + 1); 22 | recv_status = HEADER; 23 | _line_size = -1; 24 | _last_active_time = utils_tmp::get_date(); 25 | _wfd = -1; 26 | _rfd = -1; 27 | _read_ok = 1; 28 | _concat_body.clear(); 29 | _is_finished = false; 30 | LOG_WRT(Logger::INFO, std::string(BLUE_C) + "client constructor " + _ip + ":" + std::to_string(_port) + std::string(RESET)); 31 | } 32 | 33 | Client::~Client() 34 | { 35 | LOG_WRT(Logger::INFO, std::string(RED_C) + "Destructor of client " + std::to_string(_accept_fd) 36 | + " / _rfd = " + std::to_string(_rfd) 37 | + " / _wfd = " + std::to_string(_wfd) 38 | + std::string(RESET)); 39 | if (_buffermalloc) 40 | { 41 | try { free(_buffermalloc); _buffermalloc = NULL; } 42 | catch (...) { LOG_WRT(Logger::DEBUG, "double free destructor client"); } 43 | } 44 | if (_accept_fd != -1) 45 | { 46 | FD_CLR(_accept_fd, &g_conf._save_readfds); 47 | FD_CLR(_accept_fd, &g_conf._readfds); 48 | FD_CLR(_accept_fd, &g_conf._save_writefds); 49 | FD_CLR(_accept_fd, &g_conf._writefds); 50 | g_conf.remove_fd(_accept_fd); 51 | close(_accept_fd); 52 | _accept_fd = -1; 53 | } 54 | } 55 | 56 | /* 57 | ** other class methods 58 | */ 59 | 60 | void Client::reset(void) 61 | { 62 | LOG_WRT(Logger::INFO, std::string(BLUE_C) + "Destructor of client " + std::to_string(_accept_fd) 63 | + " / _rfd = " + std::to_string(_rfd) 64 | + " / _wfd = " + std::to_string(_wfd) 65 | + std::string(RESET)); 66 | 67 | _is_finished = false; 68 | _request.reset(); 69 | _response.reset(); 70 | _concat_body.clear(); 71 | FD_SET(_accept_fd, &g_conf._save_readfds); 72 | FD_CLR(_accept_fd, &g_conf._readfds); 73 | FD_CLR(_accept_fd, &g_conf._save_writefds); 74 | FD_CLR(_accept_fd, &g_conf._writefds); 75 | memset((void *)_buffermalloc, '\0', RECV_BUFFER + 1); 76 | recv_status = HEADER; 77 | _line_size = -1; 78 | if (_wfd != -1) 79 | { 80 | g_conf.remove_fd(_wfd); 81 | close(_wfd); 82 | _wfd = -1; 83 | } 84 | if (_rfd != -1) 85 | { 86 | g_conf.remove_fd(_rfd); 87 | close(_rfd); 88 | _rfd = -1; 89 | } 90 | _read_ok = 1; 91 | } 92 | 93 | void Client::write_file() 94 | { 95 | int ret; 96 | 97 | LOG_WRT(Logger::DEBUG, "write_file()"); 98 | ret = write(_wfd, _request._text_body.c_str(), _request._text_body.length()); 99 | if (ret == -1) 100 | { 101 | _response._status_code = INTERNAL_ERROR_500; 102 | return ; 103 | } 104 | else if (ret == 0) 105 | ; 106 | } 107 | 108 | void Client::read_file(std::string &buff) 109 | { 110 | int ret; 111 | int status; 112 | char buffer[BUFFER_SIZE + 1]; 113 | 114 | LOG_WRT(Logger::DEBUG, "inside read_file()"); 115 | 116 | LOG_WRT(Logger::DEBUG, "rfd = " + std::to_string(_rfd)); 117 | LOG_WRT(Logger::DEBUG, "wfd = " + std::to_string(_wfd)); 118 | 119 | waitpid((pid_t)_pid, (int *)&status, 0); 120 | 121 | LOG_WRT(Logger::DEBUG, "ok waitpid"); 122 | 123 | ret = read(_rfd, buffer, BUFFER_SIZE); 124 | if (ret == -1) 125 | { 126 | LOG_WRT(Logger::DEBUG, "KO read() ret=" + std::to_string(ret)); 127 | // exit(EXIT_FAILURE); 128 | _response._status_code = INTERNAL_ERROR_500; 129 | return ; 130 | } 131 | 132 | LOG_WRT(Logger::DEBUG, "ok read() ret=" + std::to_string(ret)); 133 | 134 | if (ret == 0) 135 | _read_ok = 1; 136 | else if (ret > 0) 137 | _read_ok = 0; 138 | 139 | if (ret >= 0 && !(_request._body_file.empty())) 140 | { 141 | buffer[ret] = '\0'; 142 | _response.build_chunked(_request, buffer, ret); 143 | _read_ok = 1; 144 | _rfd = -1; 145 | } 146 | else 147 | { 148 | buffer[ret] = '\0'; 149 | buff.append(std::string(buffer)); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /project/v1/srcs/Conf.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Conf.hpp" 2 | 3 | /* 4 | ** constructors / destructors 5 | */ 6 | 7 | Conf::Conf() 8 | { 9 | _on = true; 10 | _webserv = "webserv"; 11 | // select() related 12 | _nfds = -1; 13 | FD_ZERO(&_readfds); 14 | FD_ZERO(&_save_readfds); 15 | FD_ZERO(&_writefds); 16 | FD_ZERO(&_save_writefds); 17 | FD_ZERO(&_exceptfds); 18 | FD_ZERO(&_save_exceptfds); 19 | _timeout.tv_sec = 5; 20 | _timeout.tv_usec = 0; 21 | // debug 22 | _nb_accepted_connections = 0;} 23 | 24 | /* 25 | ** other class methods 26 | */ 27 | 28 | int Conf::parse(char *file) 29 | { 30 | Config_parser conf(file); 31 | conf.setup_server(); 32 | 33 | for (size_t i = 0; i < _servers.size(); ++i) 34 | { 35 | LOG_WRT(Logger::DEBUG, "server on"); 36 | if (!_servers[i]->start()) 37 | return (0); 38 | } 39 | return (1); 40 | } 41 | 42 | void Conf::reset_fd_sets(void) 43 | { 44 | _readfds = _save_readfds; 45 | _writefds = _save_writefds; 46 | _exceptfds = _save_exceptfds; 47 | } 48 | 49 | int Conf::get_nfds(void) const 50 | { 51 | /* 52 | ** http://manpagesfr.free.fr/man/man2/select.2.html 53 | ** 54 | ** nfds est le numéro du plus grand descripteur de fichier des 3 ensembles, plus 1. 55 | */ 56 | return (*std::max_element(_active_fds.begin(), _active_fds.end()) + 1); // https://stackoverflow.com/questions/9874802/how-can-i-get-the-max-or-min-value-in-a-vector 57 | } 58 | 59 | int Conf::get_nb_open_fds(void) const 60 | { 61 | return (_active_fds.size()); // https://stackoverflow.com/questions/9874802/how-can-i-get-the-max-or-min-value-in-a-vector 62 | } 63 | 64 | 65 | void find_fd(int fd) 66 | { 67 | std::list::iterator it_fd = g_conf._active_fds.begin(); 68 | 69 | for (; it_fd != g_conf._active_fds.end(); ++it_fd) 70 | { 71 | if (*it_fd == fd) 72 | return; 73 | } 74 | LOG_WRT(Logger::DEBUG, "error no fd " + std::to_string(fd) + "in active_fds"); 75 | } 76 | 77 | void Conf::add_fd(int fd) 78 | { 79 | if (fd >= 0) 80 | { 81 | LOG_WRT(Logger::DEBUG, "add_fd() " + std::to_string(fd)); 82 | _active_fds.push_back(fd); 83 | } 84 | else 85 | { 86 | LOG_WRT(Logger::DEBUG, "can't add_fd(-1) " + std::to_string(fd)); 87 | } 88 | } 89 | 90 | void Conf::remove_fd(int fd) 91 | { 92 | LOG_WRT(Logger::DEBUG, "remove_fd() " + std::to_string(fd)); 93 | find_fd(fd); 94 | _active_fds.remove(fd); 95 | } 96 | 97 | int Conf::run_select(void) 98 | { 99 | reset_fd_sets(); // la fonction select() exclue les fds qui ne sont pas prêts donc il faut pouvoir reconstituer le pool de fd à chaque tour de boucle 100 | 101 | LOG_WRT(Logger::INFO, "select(nfds=" + std::to_string(g_conf.get_nfds()) + ", readfds, writefds, NULL, NULL)"); 102 | return (select(get_nfds(), &_readfds, &_writefds, NULL, NULL)); // todo: quid du timeout 103 | 104 | /* 105 | ** http://manpagesfr.free.fr/man/man2/select.2.html 106 | ** 107 | ** Valeur renvoyée : En cas de réussite select() et pselect() renvoient 108 | ** le nombre de descripteurs de fichier dans les trois ensembles de descripteurs retournés 109 | ** (c'est-à-dire, le nombre total de bits à 1 dans readfds, writefds, exceptfds) 110 | ** qui peut être nul si le délai de timeout a expiré avant que quoi que ce soit d'intéressant ne se produise. 111 | ** Ils retournent -1 s'ils échouent, auquel cas errno contient le code d'erreur ; 112 | ** les ensembles et timeout ne sont plus définis, ne vous fiez plus à leur contenu après une erreur. 113 | */ 114 | } -------------------------------------------------------------------------------- /project/v1/srcs/Config_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Config_parser.hpp" 2 | 3 | Config_parser::Config_parser(char *conf) : conf(conf) 4 | { 5 | line_count = 0; 6 | } 7 | 8 | Config_parser::~Config_parser() 9 | { 10 | } 11 | 12 | void Config_parser::fail(const std::string &message) 13 | { 14 | ERROR_RET("Config_parser: " + message); 15 | if (fd != -1) 16 | close(fd); 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | void Config_parser::fail_double_token(std::string &str) 21 | { 22 | if (str.length() != 0) 23 | fail("Double token [" + std::to_string(line_count) + "]"); 24 | } 25 | void Config_parser::fail_double_token(int val) 26 | { 27 | if (val != -1) 28 | fail("Double token [" + std::to_string(line_count) + "]"); 29 | } 30 | 31 | void Config_parser::setup_server() 32 | { 33 | parse_conf(); 34 | check_conf(); 35 | 36 | for (size_t i = 0; i < serv.size(); ++i) 37 | { 38 | Server *server = new Server(serv[i].name, stoi(serv[i].port), serv[i].host, serv[i].error_page); 39 | g_conf._servers.push_back(server); 40 | 41 | LOG_WRT(Logger::DEBUG, "SERVER " + std::to_string(i)); 42 | LOG_WRT(Logger::DEBUG, "host = " + serv[i].host); 43 | LOG_WRT(Logger::DEBUG, "name = " + serv[i].name); 44 | LOG_WRT(Logger::DEBUG, "port = " + serv[i].port); 45 | LOG_WRT(Logger::DEBUG, "error_page = " + serv[i].error_page); 46 | for (size_t y = 0; y < serv[i].loc.size(); ++y) 47 | { 48 | Location *location = new Location( serv[i].loc[y].uri, serv[i].loc[y].root, serv[i].loc[y].index, 49 | serv[i].loc[y].method, serv[i].loc[y].cgi_path, serv[i].loc[y].php_path, 50 | serv[i].loc[y].cgi, serv[i].loc[y].auto_index, serv[i].loc[y].max_body, 51 | serv[i].loc[y].auth); 52 | 53 | server->_locations.push_back(location); 54 | 55 | LOG_WRT(Logger::DEBUG, " LOCATION " + std::to_string(y)); 56 | LOG_WRT(Logger::DEBUG, " uri = " + serv[i].loc[y].uri); 57 | if (serv[i].loc[y].method.size()) 58 | LOG_WRT(Logger::DEBUG, " method"); 59 | for (size_t z = 0; z < serv[i].loc[y].method.size(); z++) 60 | LOG_WRT(Logger::DEBUG, " " + serv[i].loc[y].method[z]); 61 | LOG_WRT(Logger::DEBUG, " root = " + serv[i].loc[y].root); 62 | LOG_WRT(Logger::DEBUG, " index = " + serv[i].loc[y].index); 63 | LOG_WRT(Logger::DEBUG, " cgi_path = " + serv[i].loc[y].cgi_path); 64 | LOG_WRT(Logger::DEBUG, " php_path = " + serv[i].loc[y].php_path); 65 | LOG_WRT(Logger::DEBUG, " cgi = " + serv[i].loc[y].cgi); 66 | LOG_WRT(Logger::DEBUG, " auto_index = " + std::to_string(serv[i].loc[y].auto_index)); 67 | LOG_WRT(Logger::DEBUG, " max_body = " + std::to_string(serv[i].loc[y].max_body)); 68 | LOG_WRT(Logger::DEBUG, " auth = " + serv[i].loc[y].auth); 69 | } 70 | LOG_WRT(Logger::DEBUG, ""); 71 | } 72 | } 73 | 74 | void Config_parser::check_conf() 75 | { 76 | for (size_t i = 0; i < serv.size(); ++i) 77 | { 78 | for (size_t j = 0; j < serv.size(); ++j) 79 | if (i != j && serv[i].port == serv[j].port) 80 | fail("Duplicate ports"); 81 | 82 | int is_default_root = 0; 83 | for (size_t y = 0; y < serv[i].loc.size(); ++y) 84 | { 85 | if (serv[i].loc[y].uri == "/") 86 | is_default_root = 1; 87 | for (size_t z = 0; z < serv[i].loc[y].method.size(); z++) 88 | if (!utils_tmp::is_valide_methods(serv[i].loc[y].method[z])) 89 | fail("Not valide methode"); 90 | if (serv[i].loc[y].auto_index < -1 || serv[i].loc[y].auto_index > 1) 91 | fail("auto_index unvalid (on = 1 / off = 0)"); 92 | } 93 | if (!is_default_root) 94 | fail("Server need default location root"); 95 | } 96 | } 97 | 98 | void Config_parser::parse_conf() 99 | { 100 | int ret; 101 | char *cline; 102 | std::string line; 103 | 104 | if (conf == NULL) 105 | fail("unvalib file"); 106 | if ((fd = open(conf, O_RDONLY)) == -1) 107 | fail("fail to open config file"); 108 | while ((ret = get_next_line(fd, &cline)) && ++line_count) 109 | { 110 | line = cline; 111 | free(cline); 112 | cline = NULL; 113 | line.erase(std::remove_if(line.begin(), line.end(), utils_tmp::isspace), line.end()); 114 | if (line.length() == 7 && line.compare("server{") == 0) 115 | parse_server(); 116 | else if (line[0] == '#') 117 | continue ; 118 | else if (line.length() != 0) 119 | fail("bad syntax [" + std::to_string(line_count) + "]"); 120 | } 121 | if (cline) 122 | free(cline); 123 | if (ret < 0) 124 | fail("fail to read config file"); 125 | } 126 | 127 | void Config_parser::parse_server() 128 | { 129 | int ret; 130 | char *cline; 131 | std::string line; 132 | t_serv new_serv; 133 | 134 | while ((ret = get_next_line(fd, &cline)) && ++line_count) 135 | { 136 | line = cline; 137 | free(cline); 138 | cline = NULL; 139 | std::vector tokens= utils_tmp::split_string(line, WHITE_SPACE); 140 | if (tokens.size() == 1 && tokens[0] == "}") 141 | { 142 | serv.push_back(new_serv); 143 | return ; 144 | } 145 | else if (tokens.size() == 0 || tokens[0][0] == '#') 146 | continue ; 147 | else if (tokens[0] == "location") 148 | parse_location(tokens, new_serv); 149 | else 150 | add_serv_values(tokens, new_serv); 151 | } 152 | if (cline) 153 | free(cline); 154 | if (ret < 0) 155 | fail("fail to read config file"); 156 | fail("Bracket not close [" + std::to_string(line_count) + "]"); 157 | } 158 | 159 | void Config_parser::parse_location(std::vector &token, t_serv &serv) 160 | { 161 | int ret; 162 | char *cline; 163 | std::string line; 164 | t_loc new_loc; 165 | 166 | // init 167 | new_loc.max_body = -1; 168 | if (!(token.size() == 3 && token[2][0] == '{') 169 | && !(token.size() == 2 && token[1][token[1].length() - 1] == '{')) 170 | fail("Bad location: " + line + "[" + std::to_string(line_count) + "]"); 171 | new_loc.uri = token[1]; 172 | while ((ret = get_next_line(fd, &cline)) && ++line_count) 173 | { 174 | line = cline; 175 | free(cline); 176 | cline = NULL; 177 | std::vector tokens= utils_tmp::split_string(line, WHITE_SPACE); 178 | if (tokens.size() == 1 && tokens[0] == "}") 179 | { 180 | serv.loc.push_back(new_loc); 181 | return ; 182 | } 183 | else if (tokens.size() == 0 || tokens[0][0] == '#') 184 | continue ; 185 | else 186 | add_loc_values(tokens, new_loc); 187 | } 188 | if (cline) 189 | free(cline); 190 | if (ret < 0) 191 | fail("fail to read config file"); 192 | fail("Bracket not close [" + std::to_string(line_count) + "]"); 193 | } 194 | 195 | void Config_parser::add_serv_values(std::vector &tokens, t_serv &serv) 196 | { 197 | if (tokens.size() == 1) 198 | fail("no arguments given [" + std::to_string(line_count) + "]"); 199 | if (tokens.size() > 2 && tokens[2][0] != '#') 200 | fail("to many arguments [" + std::to_string(line_count) + "]"); 201 | 202 | if (tokens[0] == _HOST) 203 | { 204 | fail_double_token(serv.host); 205 | serv.host = tokens[1]; 206 | } 207 | else if (tokens[0] == _NAME) 208 | { 209 | fail_double_token(serv.name); 210 | serv.name = tokens[1]; 211 | } 212 | else if (tokens[0] == _PORT) 213 | { 214 | fail_double_token(serv.port); 215 | serv.port = tokens[1]; 216 | } 217 | else if (tokens[0] == _ERROR_PAGE) 218 | { 219 | fail_double_token(serv.error_page); 220 | serv.error_page = tokens[1]; 221 | } 222 | else 223 | fail("Token invalid (" + tokens[0] + ") [" + std::to_string(line_count) + "]"); 224 | } 225 | 226 | void Config_parser::add_loc_values(std::vector &tokens, t_loc &loc) 227 | { 228 | if (tokens.size() == 1) 229 | fail("no arguments given [" + std::to_string(line_count) + "]"); 230 | 231 | if (tokens[0] == _METHOD) 232 | { 233 | if (loc.method.size() != 0) 234 | fail("Double token [" + std::to_string(line_count) + "]"); 235 | for (size_t i = 1; i < tokens.size(); ++i) 236 | loc.method.push_back(tokens[i]); 237 | } 238 | else 239 | { 240 | if (tokens.size() > 2 && tokens[2][0] != '#') 241 | fail("too many arguments [" + std::to_string(line_count) + "]"); 242 | if (tokens[0] == _ROOT) 243 | { 244 | fail_double_token(loc.root); 245 | loc.root = tokens[1]; 246 | } 247 | else if (tokens[0] == _INDEX) 248 | { 249 | fail_double_token(loc.index); 250 | loc.index = tokens[1]; 251 | } 252 | else if (tokens[0] == _CGI_PATH) 253 | { 254 | fail_double_token(loc.cgi_path); 255 | loc.cgi_path = tokens[1]; 256 | } 257 | else if (tokens[0] == _PHP_PATH) 258 | { 259 | fail_double_token(loc.php_path); 260 | loc.php_path = tokens[1]; 261 | } 262 | else if (tokens[0] == _CGI) 263 | { 264 | fail_double_token(loc.cgi); 265 | loc.cgi = tokens[1]; 266 | } 267 | else if (tokens[0] == _AUTO_INDEX) 268 | { 269 | fail_double_token(loc.auto_index); 270 | loc.auto_index = stoi(tokens[1]); 271 | } 272 | else if (tokens[0] == _MAX_BODY) 273 | { 274 | fail_double_token(loc.max_body); 275 | loc.max_body = stoi(tokens[1]); 276 | } 277 | else if (tokens[0] == _AUTH) 278 | { 279 | fail_double_token(loc.auth); 280 | loc.auth = tokens[1]; 281 | } 282 | else 283 | fail("Token invalid (" + tokens[0] + ") [" + std::to_string(line_count) + "]"); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /project/v1/srcs/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Logger.hpp" 2 | 3 | Logger Logger::instance; 4 | 5 | Logger::Logger() : active(false) 6 | { 7 | } 8 | 9 | Logger::~Logger() 10 | { 11 | instance.Stop(); 12 | } 13 | 14 | const std::string Logger::PRIORITY_NAMES[] = 15 | { 16 | "DEBUG", 17 | "INFO", 18 | "ERROR" 19 | }; 20 | 21 | void Logger::Start(Priority minPriority, const std::string &logFile, bool date) 22 | { 23 | instance.active = true; 24 | instance.isdate = date; 25 | instance.minPriority = minPriority; 26 | instance.request = 0; 27 | if (logFile != "") 28 | { 29 | std::string file_name = "./log/log_" + std::to_string(instance.request) + ".txt"; 30 | instance.fileStream.open(file_name); 31 | } 32 | } 33 | 34 | void Logger::Stop() 35 | { 36 | instance.active = false; 37 | if (instance.fileStream.is_open()) 38 | instance.fileStream.close(); 39 | } 40 | 41 | void Logger::ChangeFile(void) 42 | { 43 | instance.request++; 44 | if (instance.fileStream.is_open()) 45 | { 46 | std::string file_name = "./log/log_" + std::to_string(instance.request) + ".txt"; 47 | instance.fileStream.close(); 48 | instance.fileStream.open(file_name); 49 | } 50 | } 51 | 52 | void Logger::Write(Priority priority, const std::string &message) 53 | { 54 | if (instance.active && priority >= instance.minPriority) 55 | { 56 | std::ostream& stream = instance.fileStream.is_open() ? instance.fileStream : std::cout; 57 | 58 | stream << PRIORITY_NAMES[priority] 59 | << std::setw(7 - (PRIORITY_NAMES[priority]).length()); 60 | if (instance.isdate) 61 | { 62 | stream << " [" 63 | << utils_tmp::get_date() 64 | << "]"; 65 | } 66 | stream << ": " 67 | << message 68 | << std::endl; 69 | } 70 | } 71 | 72 | int Logger::Error(const std::string &message) 73 | { 74 | if (errno != 0) 75 | Logger::Write(Logger::ERROR, message + " -> (" + std::string(strerror(errno)) + ")"); 76 | else 77 | Logger::Write(Logger::ERROR, message); 78 | 79 | return (EXIT_FAILURE); 80 | } 81 | -------------------------------------------------------------------------------- /project/v1/srcs/Response.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Headers.hpp" 2 | 3 | /* 4 | ** constructors / destructors 5 | */ 6 | 7 | Response::Response(void) 8 | { 9 | init(); 10 | } 11 | 12 | /* 13 | ** private class methods 14 | */ 15 | 16 | void Response::init(void) 17 | { 18 | // Status Line 19 | _http_version.clear(); 20 | _status_code = 0; 21 | _reason_phrase.clear(); 22 | _allow.clear(); 23 | _content_language.clear(); 24 | _content_length = 0; 25 | _content_location.clear(); 26 | _content_type.clear(); 27 | _last_modified.clear(); 28 | _location.clear(); 29 | _date.clear(); 30 | _retry_after = -1; 31 | _server.clear(); 32 | _transfer_encoding.clear(); 33 | _www_authenticate.clear(); 34 | // Request body 35 | _body.clear(); 36 | // Content to write in fd 37 | _to_send.clear(); 38 | // Other headers 39 | _keep_alive.clear(); 40 | // Other 41 | _bytes_send = 0; 42 | send_status = HANDLE_RESPONSE; 43 | read_fd = -1; 44 | } 45 | 46 | // int Response::concat_to_send(void) 47 | // { 48 | // size_t i; 49 | 50 | // // 1. Status Line 51 | // _to_send += _http_version + " "; 52 | // _to_send += std::to_string(_status_code) + " "; 53 | // _to_send += _reason_phrase + "\r\n"; 54 | 55 | // // 2. Request Headers, dans l'ordre du sujet 56 | // if (_content_length >= 0) 57 | // { 58 | // _to_send += "Content-Length: " + std::to_string(_content_length) + "\r\n"; 59 | // } 60 | 61 | // if (!_allow.empty()) 62 | // { 63 | // _to_send += "allow: " + _allow + "\r\n"; 64 | // } 65 | // if (!_last_modified.empty()) 66 | // { 67 | // _to_send += "Last-Modified: " + _last_modified + "\r\n"; 68 | // } 69 | // if (_retry_after >= 0) 70 | // { 71 | // _to_send += "Retry-After: " + std::to_string(_retry_after) + "\r\n"; 72 | // } 73 | // if (!_location.empty() && _status_code == 201) 74 | // { 75 | // _to_send += "Location: " + _location + "\r\n"; 76 | // } 77 | // if (!_date.empty()) 78 | // { 79 | // _to_send += "Date: " + _date + "\r\n"; 80 | // } 81 | // if (!_server.empty()) 82 | // { 83 | // _to_send += "Server: " + _server + "\r\n"; 84 | // } 85 | // if (!_transfer_encoding.empty()) 86 | // { 87 | // _to_send += "Transfer-Encoding: " + _transfer_encoding + "\r\n"; 88 | // } 89 | // if (!_content_type.empty()) 90 | // { 91 | // _to_send += "Content-Type:"; 92 | // i = 0; 93 | // while (i < _content_type.size()) 94 | // { 95 | // _to_send += " " + _content_type[i]; 96 | // if (i + 1 < _content_type.size()) 97 | // _to_send += ";"; 98 | // i++; 99 | // } 100 | // _to_send += "\r\n"; 101 | // } 102 | 103 | // // 3. Request body 104 | // _to_send += "\r\n"; 105 | // if (!_body.empty()) 106 | // { 107 | // _to_send += _body; 108 | // LOG_WRT(Logger::DEBUG, "BODY EMPTY IN RESPONSE"); 109 | // } 110 | // return (1); 111 | // } 112 | 113 | 114 | int Response::concat_to_send(void) 115 | { 116 | size_t i; 117 | 118 | // 1. Status Line 119 | _to_send.append(_http_version); 120 | _to_send.append(" "); 121 | _to_send.append(std::to_string(_status_code)); 122 | _to_send.append(" "); 123 | _to_send.append(_reason_phrase); 124 | _to_send.append("\r\n"); 125 | 126 | // 2. Request Headers, dans l'ordre du sujet 127 | if (_content_length >= 0) 128 | { 129 | _to_send.append("Content-Length: "); 130 | _to_send.append(std::to_string(_content_length)); 131 | _to_send.append("\r\n"); 132 | } 133 | 134 | if (!_allow.empty()) 135 | { 136 | _to_send.append("allow: "); 137 | _to_send.append(_allow); 138 | _to_send.append("\r\n"); 139 | } 140 | if (!_last_modified.empty()) 141 | { 142 | _to_send.append("Last-Modified: "); 143 | _to_send.append(_last_modified); 144 | _to_send.append("\r\n"); 145 | } 146 | if (_retry_after >= 0) 147 | { 148 | _to_send.append("Retry-After: "); 149 | _to_send.append(std::to_string(_retry_after)); 150 | _to_send.append("\r\n"); 151 | } 152 | if (!_location.empty() && _status_code == 201) 153 | { 154 | _to_send.append("Location: "); 155 | _to_send.append(_location); 156 | _to_send.append("\r\n"); 157 | } 158 | if (!_date.empty()) 159 | { 160 | _to_send.append("Date: "); 161 | _to_send.append(_date); 162 | _to_send.append("\r\n"); 163 | } 164 | if (!_server.empty()) 165 | { 166 | _to_send.append("Server: "); 167 | _to_send.append(_server); 168 | _to_send.append("\r\n"); 169 | } 170 | if (!_transfer_encoding.empty()) 171 | { 172 | _to_send.append("Transfer-Encoding: "); 173 | _to_send.append(_transfer_encoding); 174 | _to_send.append("\r\n"); 175 | } 176 | if (!_content_type.empty()) 177 | { 178 | _to_send.append("Content-Type:"); 179 | i = 0; 180 | while (i < _content_type.size()) 181 | { 182 | _to_send.append(" "); 183 | _to_send.append(_content_type[i]); 184 | if (i + 1 < _content_type.size()) 185 | _to_send.append(";"); 186 | i++; 187 | } 188 | _to_send.append("\r\n"); 189 | } 190 | 191 | // 3. Request body 192 | _to_send.append("\r\n"); 193 | if (!_body.empty()) 194 | { 195 | _to_send.append(_body); 196 | LOG_WRT(Logger::DEBUG, "BODY EMPTY IN RESPONSE"); 197 | } 198 | return (1); 199 | } 200 | 201 | /* 202 | ** public class methods 203 | */ 204 | 205 | int Response::format_to_send(Request *req) 206 | { 207 | // Status Line 208 | _http_version = "HTTP/1.1"; 209 | _reason_phrase = code_to_reason[_status_code]; 210 | 211 | _content_length = _body.length(); // https://stackoverflow.com/questions/13821263/should-newline-be-included-in-http-response-content-length 212 | 213 | // Response headers, dans l'ordre du sujet 214 | _date = get_date(); 215 | _server = g_conf._webserv; 216 | if ((req->_method == "GET" || req->_method == "HEAD" || req->_method == "PUT" || req->_method == "POST") && (_status_code == OK_200 || _status_code == CREATED_201) && _content_type[0] == "") 217 | _content_type[0] = get_content_type(req->_file); 218 | if (_status_code == CREATED_201) 219 | _location = get_location_header(req); 220 | _content_language.clear(); 221 | _content_location.clear(); 222 | if (!req->_body_file.empty()) 223 | _transfer_encoding = "chunked"; 224 | else 225 | _transfer_encoding.clear(); 226 | _www_authenticate.clear(); 227 | // Response body 228 | if (req->_method == "HEAD") 229 | _body.clear(); 230 | concat_to_send(); 231 | return (1); 232 | } 233 | 234 | void Response::handle_response(Request *req) 235 | { 236 | if (service_unavailable(req)) // 503 237 | return; 238 | else if (bad_request(req)) // 400 239 | return; 240 | else if (method_not_allowed(req)) // 405 241 | return; 242 | else if (unauthorized(req)) // 401 243 | return; 244 | else if (request_entity_too_large(req)) // 413 245 | return; 246 | else if (_status_code == NOT_FOUND_404) // 404 247 | not_found(req); 248 | else if (req->_method == "GET" || req->_method == "HEAD") 249 | get(req); 250 | else if (req->_method == "POST") 251 | post(req); 252 | else if (req->_method == "PUT") 253 | put(req); 254 | else if (req->_method == "DELETE") 255 | ft_delete(req); 256 | else if (req->_method == "OPTIONS") 257 | option(req); 258 | else if (req->_method == "TRACE") 259 | trace(req); 260 | else if (req->_method == "CONNECT") 261 | connect(req); 262 | } 263 | 264 | int Response::accepted_method(Request *req) 265 | { 266 | return (req->_method == "GET" || req->_method == "HEAD" || req->_method == "PUT" || req->_method == "POST" || req->_method == "DELETE" || req->_method == "OPTIONS" || req->_method == "TRACE" || req->_method == "CONNECT"); 267 | } 268 | 269 | int Response::bad_request(Request *req) 270 | { 271 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 272 | { 273 | if (accepted_method(req)) 274 | return (0); 275 | LOG_WRT(Logger::DEBUG, "BAD_REQUEST_400\n"); 276 | _status_code = BAD_REQUEST_400; 277 | _allow = vector_to_string(req->_location->_method, ','); 278 | std::string path = std::string(_client->_server->_error + "/400.html"); 279 | req->_client->_rfd = open(path.c_str(), O_RDONLY); 280 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 281 | g_conf.add_fd(req->_client->_rfd); 282 | return (1); 283 | } 284 | else 285 | { 286 | if (accepted_method(req)) 287 | return (0); 288 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 289 | g_conf.remove_fd(req->_client->_rfd); 290 | close(req->_client->_rfd); 291 | req->_client->_rfd = -1; 292 | return (1); 293 | } 294 | } 295 | 296 | int Response::method_not_allowed(Request *req) 297 | { 298 | LOG_WRT(Logger::DEBUG, "method_not_allowed()"); 299 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 300 | { 301 | for (std::size_t i = 0; i < (req->_location->_method).size(); ++i) 302 | { 303 | LOG_WRT(Logger::DEBUG, "test if " + (req->_location->_method)[i] + " == " + req->_method); 304 | if ((req->_location->_method)[i] == req->_method) 305 | { 306 | LOG_WRT(Logger::DEBUG, "method " + req->_method + " is allowed"); 307 | return (0); 308 | } 309 | } 310 | LOG_WRT(Logger::DEBUG, "METHOD_NOT_ALLOWED_405\n"); 311 | _status_code = METHOD_NOT_ALLOWED_405; 312 | _allow = vector_to_string(req->_location->_method, ','); 313 | std::string path = std::string(_client->_server->_error + "/405.html"); 314 | req->_client->_rfd = open(path.c_str(), O_RDONLY); 315 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 316 | g_conf.add_fd(req->_client->_rfd); 317 | return (1); 318 | } 319 | else 320 | { 321 | for (std::size_t i = 0; i < (req->_location->_method).size(); ++i) 322 | { 323 | LOG_WRT(Logger::DEBUG, "test if " + (req->_location->_method)[i] + " == " + req->_method); 324 | if ((req->_location->_method)[i] == req->_method) 325 | return (0); 326 | } 327 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 328 | g_conf.remove_fd(req->_client->_rfd); 329 | close(req->_client->_rfd); 330 | req->_client->_rfd = -1; 331 | return (1); 332 | } 333 | } 334 | 335 | // https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c 336 | 337 | static const std::string base64_chars = 338 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 339 | "abcdefghijklmnopqrstuvwxyz" 340 | "0123456789+/"; 341 | 342 | static inline bool is_base64(unsigned char c) 343 | { 344 | return (isalnum(c) || (c == '+') || (c == '/')); 345 | } 346 | 347 | std::string base64_decode(std::string const &encoded_string) 348 | { 349 | int in_len = encoded_string.size(); 350 | int i = 0; 351 | int j = 0; 352 | int in_ = 0; 353 | unsigned char char_array_4[4], char_array_3[3]; 354 | std::string ret; 355 | 356 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) 357 | { 358 | char_array_4[i++] = encoded_string[in_]; 359 | in_++; 360 | if (i == 4) 361 | { 362 | for (i = 0; i < 4; i++) 363 | char_array_4[i] = base64_chars.find(char_array_4[i]); 364 | 365 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 366 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 367 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 368 | 369 | for (i = 0; (i < 3); i++) 370 | ret += char_array_3[i]; 371 | i = 0; 372 | } 373 | } 374 | if (i) 375 | { 376 | for (j = i; j < 4; j++) 377 | char_array_4[j] = 0; 378 | 379 | for (j = 0; j < 4; j++) 380 | char_array_4[j] = base64_chars.find(char_array_4[j]); 381 | 382 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 383 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 384 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 385 | 386 | for (j = 0; (j < i - 1); j++) 387 | ret += char_array_3[j]; 388 | } 389 | 390 | return ret; 391 | } 392 | 393 | int Response::unauthorized(Request *req) 394 | { 395 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 396 | { 397 | if (!req->_location->_auth.empty()) 398 | { 399 | LOG_WRT(Logger::DEBUG, "inside unauthorized():"); 400 | 401 | std::vector tokens; 402 | tokens = utils_tmp::split(req->_authorization, ' '); 403 | std::string creds = tokens[1]; 404 | if (req->_location->_auth == base64_decode(creds)) 405 | { 406 | LOG_WRT(Logger::INFO, "Response::unauthorized() ? -> ok authorized\n"); 407 | _status_code = OK_200; 408 | return (0); 409 | } 410 | else 411 | { 412 | LOG_WRT(Logger::INFO, "Response::unauthorized() ? -> ko unauthorized\n"); 413 | _status_code = UNAUTHORIZED_401; 414 | std::string path = std::string(_client->_server->_error + "/401.html"); 415 | req->_client->_rfd = open(path.c_str(), O_RDONLY); 416 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 417 | g_conf.add_fd(req->_client->_rfd); 418 | return (1); 419 | } 420 | } 421 | else 422 | return (0); 423 | } 424 | else 425 | { 426 | if (!req->_location->_auth.empty()) 427 | { 428 | std::vector tokens; 429 | tokens = utils_tmp::split(req->_authorization, ' '); 430 | std::string creds = tokens[1]; 431 | if (req->_location->_auth == base64_decode(creds)) 432 | { 433 | _status_code = OK_200; 434 | return (0); 435 | } 436 | else 437 | { 438 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 439 | g_conf.remove_fd(req->_client->_rfd); 440 | close(req->_client->_rfd); 441 | req->_client->_rfd = -1; 442 | return (1); 443 | } 444 | } 445 | else 446 | return (0); 447 | } 448 | } 449 | 450 | int Response::service_unavailable(Request *req) 451 | { 452 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 453 | { 454 | if (_retry_after == UNAVAILABLE_TIME) 455 | { 456 | LOG_WRT(Logger::INFO, "Response::service_unavailable()\n"); 457 | _status_code = SERVICE_UNAVAILABLE_503; 458 | std::string path = std::string(_client->_server->_error + "/503.html"); 459 | req->_client->_rfd = open(path.c_str(), O_RDONLY); 460 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 461 | g_conf.add_fd(req->_client->_rfd); 462 | return (1); 463 | } 464 | else 465 | return (0); 466 | } 467 | else 468 | { 469 | if (_retry_after == UNAVAILABLE_TIME) 470 | { 471 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 472 | g_conf.remove_fd(req->_client->_rfd); 473 | close(req->_client->_rfd); 474 | req->_client->_rfd = -1; 475 | return (1); 476 | } 477 | else 478 | return (0); 479 | } 480 | } 481 | 482 | int Response::request_entity_too_large(Request *req) 483 | { 484 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 485 | { 486 | if (req->_saved_error == REQUEST_ENTITY_TOO_LARGE_413 || (req->_content_length > 0 && req->_location->_max_body > 0)) 487 | { 488 | if (req->_saved_error == REQUEST_ENTITY_TOO_LARGE_413 || (req->_content_length > req->_location->_max_body)) 489 | { 490 | LOG_WRT(Logger::INFO, "Response::request_entity_too_large()\n"); 491 | _status_code = REQUEST_ENTITY_TOO_LARGE_413; 492 | std::string path = std::string(_client->_server->_error + "/413.html"); 493 | req->_client->_rfd = open(path.c_str(), O_RDONLY); 494 | LOG_WRT(Logger::DEBUG, "Response::request_entity_too_large() path =" + path + " | _rfd =" + std::to_string(req->_client->_rfd) + "\n"); 495 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 496 | g_conf.add_fd(req->_client->_rfd); 497 | return (1); 498 | } 499 | } 500 | } 501 | else 502 | { 503 | if (req->_saved_error == REQUEST_ENTITY_TOO_LARGE_413 || (req->_content_length > 0 && req->_location->_max_body > 0)) 504 | { 505 | if (req->_saved_error == REQUEST_ENTITY_TOO_LARGE_413 || (req->_content_length > req->_location->_max_body)) 506 | { 507 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 508 | g_conf.remove_fd(req->_client->_rfd); 509 | close(req->_client->_rfd); 510 | req->_client->_rfd = -1; 511 | return (1); 512 | } 513 | } 514 | } 515 | return (0); 516 | } 517 | 518 | int Response::not_found(Request *req) 519 | { 520 | LOG_WRT(Logger::DEBUG, "inside notfound()"); 521 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 522 | { 523 | _status_code = NOT_FOUND_404; 524 | std::string path = std::string(_client->_server->_error + "/404.html"); 525 | req->_client->_rfd = open(path.c_str(), O_RDONLY); 526 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 527 | g_conf.add_fd(req->_client->_rfd); 528 | } 529 | else 530 | { 531 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 532 | g_conf.remove_fd(req->_client->_rfd); 533 | close(req->_client->_rfd); 534 | req->_client->_rfd = -1; 535 | } 536 | return (1); 537 | } 538 | 539 | int Response::build_chunked(Request &req, char *buffer, int ret) 540 | { 541 | std::string tmp; 542 | tmp = buffer; 543 | if (req._is_body_file_header) 544 | { 545 | utils_tmp::extract_body(tmp); 546 | ret = tmp.length(); 547 | req._is_body_file_header = false; 548 | } 549 | _to_send.append(utils_tmp::dec_to_hex(ret) + "\r\n"); 550 | _to_send.append(tmp); 551 | _to_send.append("\r\n"); 552 | LOG_WRT(Logger::DEBUG, "_to_send ==>[" + std::to_string(_to_send.length()) + "]"); 553 | if (ret == 0) 554 | { 555 | FD_CLR(_client->_rfd, &g_conf._save_readfds); 556 | print_all_fd(); 557 | g_conf.remove_fd(_client->_rfd); 558 | print_all_fd(); 559 | close(read_fd); 560 | read_fd = -1; 561 | _client->_rfd = -1; 562 | _client->_is_finished = true; 563 | remove(req._body_file.c_str()); 564 | } 565 | return (0); 566 | } 567 | 568 | /* 569 | ** Reset before waiting for new call 570 | */ 571 | 572 | void Response::reset(void) 573 | { 574 | init(); 575 | } 576 | -------------------------------------------------------------------------------- /project/v1/srcs/Server.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Headers.hpp" 2 | 3 | /* 4 | ** constructors / destructors 5 | */ 6 | 7 | Server::Server(std::string serverName, int port, std::string host, std::string error_page): 8 | _name(serverName), _port(port), _socket_fd(-1), _error(error_page), _host(host) 9 | { 10 | bzero(&_addr, sizeof(_addr)); 11 | } 12 | 13 | Server::~Server() 14 | { 15 | LOG_WRT(Logger::INFO, _name + " killed"); 16 | FD_CLR(_socket_fd, &g_conf._save_readfds); 17 | g_conf.remove_fd(_socket_fd); 18 | } 19 | 20 | /* 21 | ** other class methods 22 | */ 23 | 24 | int Server::start(void) 25 | { 26 | errno = 0; 27 | 28 | // socket 29 | if ((_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 30 | { 31 | LOG_WRT(Logger::ERROR, "Server::start -> socket(): " + std::string(strerror(errno))); 32 | return (0); 33 | } 34 | else 35 | LOG_WRT(Logger::INFO, _name + "(" + std::to_string(_port) + ") -> socket=" + std::to_string(_socket_fd)); 36 | // SO_REUSEADDR option on the listening socket: to avoid “Address already in use” error when binding(). 37 | // How to use setsockopt() with the SO_REUSEADDR option? 38 | // https://stackoverflow.com/questions/21515946/what-is-sol-socket-used-for 39 | int value = 1; // This will set the SO_REUSEADDR in my socket to 1. 40 | if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) == -1) 41 | { 42 | LOG_WRT(Logger::ERROR, "Server::start -> setsockopt(): " + std::string(strerror(errno))); 43 | return (0); 44 | } 45 | else 46 | LOG_WRT(Logger::INFO, _name + "(" + std::to_string(_port) + ") -> setsockopt=OK"); 47 | // set struct sockaddr_in 48 | _addr.sin_family = AF_INET; 49 | _addr.sin_addr.s_addr = htonl(INADDR_ANY); // https://stackoverflow.com/questions/16508685/understanding-inaddr-any-for-socket-programming 50 | _addr.sin_port = htons(_port); 51 | 52 | if (bind(_socket_fd, (struct sockaddr *)&_addr, sizeof(_addr)) == -1) 53 | { 54 | LOG_WRT(Logger::ERROR, "Server::start -> bind(): " + std::string(strerror(errno))); 55 | return (0); 56 | } 57 | else 58 | LOG_WRT(Logger::INFO, _name + "(" + std::to_string(_port) + ") -> bind=OK"); 59 | 60 | if (listen(_socket_fd, 242) == -1) 61 | { 62 | LOG_WRT(Logger::ERROR, "Server::start -> listen(): " + std::string(strerror(errno))); 63 | return (0); 64 | } 65 | else 66 | LOG_WRT(Logger::INFO, _name + "(" + std::to_string(_port) + ") -> listen=OK"); 67 | // http://beej.us/guide/bgnet/html/#selectman 68 | // Note for Linux users: Linux’s select() can return “ready-to-read” and then not actually be ready to read, thus causing the subsequent read() call to block. 69 | // You can work around this bug by setting O_NONBLOCK flag on the receiving socket so it errors with EWOULDBLOCK, then ignoring this error if it occurs. 70 | // See the fcntl() reference page for more info on setting a socket to non-blocking. 71 | // fcntl - Manipuler un descripteur de fichier 72 | // O_NONBLOCK ou O_NDELAY : Le fichier est ouvert en mode « non bloquant ». 73 | // Ni la fonction open() ni aucune autre opération ultérieure sur ce fichier ne laissera le processus appelant en attente. 74 | // Pour la manipulation des FIFO (tubes nommés), voir également fifo(7). Pour une discussion sur l'effet de O_NONBLOCK conjointement aux verrouillages de fichier impératifs et aux baux de fichiers, voir fcntl(2). 75 | if (fcntl(_socket_fd, F_SETFL, O_NONBLOCK) == -1) 76 | { 77 | LOG_WRT(Logger::ERROR, "Server::start -> fcntl(): " + std::string(strerror(errno))); 78 | return (0); 79 | } 80 | else 81 | LOG_WRT(Logger::INFO, _name + "(" + std::to_string(_port) + ") -> fcntl=OK"); 82 | 83 | FD_SET(_socket_fd, &g_conf._save_readfds); 84 | g_conf.add_fd(_socket_fd); 85 | 86 | LOG_WRT(Logger::INFO, "****************\n\n"); 87 | return (1); 88 | } 89 | 90 | int Server::acceptNewClient(void) 91 | { 92 | int accept_fd = 0; 93 | struct sockaddr_in client_addr; 94 | int addrlen = sizeof(client_addr); 95 | 96 | LOG_WRT(Logger::INFO, std::string(MAGENTA_C) + "get_nb_open_fds() = " + std::to_string(g_conf.get_nb_open_fds()) + " | OPEN_MAX = " + std::to_string(OPEN_MAX) + std::string(RESET)); 97 | 98 | bzero(&client_addr, sizeof(client_addr)); 99 | if ((accept_fd = accept(_socket_fd, (struct sockaddr *)&client_addr, (socklen_t*)&addrlen)) == -1) 100 | { 101 | LOG_WRT(Logger::ERROR, "Server::acceptNewClient -> accept(): " + std::string(strerror(errno))); 102 | return (0); 103 | } 104 | else 105 | { 106 | LOG_WRT(Logger::DEBUG, _name + "(" + std::to_string(_port) + ") -> accept_fd = " + std::to_string(accept_fd)); 107 | // fcntl(accept_fd, F_SETFL, O_NONBLOCK); 108 | Client *c = new Client(this, accept_fd, client_addr); 109 | if (g_conf.get_nb_open_fds() > 256) 110 | { 111 | LOG_WRT(Logger::INFO, std::string(MAGENTA_C) 112 | + "service unavailable (503) on " 113 | + _name 114 | + " get_nb_open_fds() = " 115 | + std::to_string(g_conf.get_nb_open_fds()) 116 | + std::string(RESET)); 117 | 118 | FD_CLR(c->_accept_fd, &g_conf._save_readfds); // no need to read request since response is same: 503 119 | FD_SET(c->_accept_fd, &g_conf._save_writefds); // need to know when response can be written in _accept_fd 120 | c->_response._retry_after = UNAVAILABLE_TIME; 121 | c->recv_status = Client::COMPLETE; 122 | _clients_503.push_back(c); 123 | } 124 | else 125 | { 126 | _clients.push_back(c); 127 | LOG_WRT(Logger::INFO, _name + " has now " + std::to_string(_clients.size()) + " clients connected"); 128 | } 129 | return (1); 130 | } 131 | } 132 | 133 | int Server::recvRequest(Client *c) 134 | { 135 | int ret = 0; 136 | errno = 0; 137 | int bytes; 138 | 139 | LOG_WRT(Logger::DEBUG, "--- start recv() ---"); 140 | // https://stackoverflow.com/questions/13736064/recv-connection-reset-by-peer 141 | // 'Connection reset by peer' has a number of causes, 142 | // but the most common one is that you have written to a connection that has already been closed by the peer. 143 | // In other words, an application protocol error. 144 | 145 | // https://stackoverflow.com/questions/24916937/how-to-catch-a-connection-reset-by-peer-error-in-c-socket 146 | // If you use non-blocking sockets, you also have to handle EWOULDBLOCK / EAGAIN here 147 | 148 | // https://stackoverflow.com/questions/49049430/difference-between-eagain-or-ewouldblock 149 | // - EWOULDBLOCK was defined for "operation would block" - that is, the operation would have blocked, but the descriptor was placed in non-blocking mode 150 | // - EAGAIN originally indicated when a "temporary resource shortage made an operation impossible" 151 | // Because the resource shortage was expected to be temporary, a subsequent attempt to perform the action might succeed (hence the name "again"). 152 | 153 | // If we try to read from a non-blocking socket and there's no data there, 154 | // it's not allowed to block-it will return -1 and errno will be set to EWOULDBLOCK. 155 | // The non-blocking mode is set by changing one of the socket's flags. 156 | 157 | bytes = strlen(c->_buffermalloc); 158 | ret = recv(c->_accept_fd, c->_buffermalloc + bytes, RECV_BUFFER - bytes, 0); 159 | bytes += ret; 160 | 161 | LOG_WRT(Logger::DEBUG, "recv() -> ret = " + std::to_string(ret) + " | bytes = " + std::to_string(bytes)); 162 | LOG_WRT(Logger::DEBUG, "c->recv_status = " + std::to_string(c->recv_status)); 163 | if (ret == -1) 164 | { 165 | LOG_WRT(Logger::ERROR, "Server::recvRequest -> recv(): " + std::string(strerror(errno))); 166 | // https://stackoverflow.com/questions/10318191/reading-socket-eagain-resource-temporarily-unavailable 167 | // EAGAIN does not mean you're disconnected, it just means "there's nothing to read now; try again later". 168 | c->_is_connected = false; 169 | return (0); 170 | } 171 | else if (ret == 0) 172 | { 173 | LOG_WRT(Logger::INFO, _name + "(" + std::to_string(_port) + ") -> client(" + std::to_string(c->_accept_fd) + ") closed"); 174 | c->_is_connected = false; 175 | return (0); 176 | } 177 | else 178 | { 179 | c->_last_active_time = utils_tmp::get_date(); 180 | LOG_WRT(Logger::DEBUG, "ret > 0"); 181 | c->_buffermalloc[bytes] = '\0'; 182 | if (c->recv_status == Client::HEADER) 183 | { 184 | LOG_WRT(Logger::DEBUG, "c->recv_status == HEADER"); 185 | if (strstr(c->_buffermalloc, "\r\n\r\n") != NULL) 186 | { 187 | LOG_WRT(Logger::DEBUG, "found \"\\r\\n\\r\\n\""); 188 | c->_request._buffer = std::string(c->_buffermalloc, bytes); 189 | LOG_WRT(Logger::DEBUG, "RAW REQUEST (" + std::to_string(bytes) + "):\n---\n" + std::string(c->_request._buffer) + "---"); 190 | c->_request.parse(_locations); 191 | if (c->_request._transfer_encoding == "chunked" || c->_request._content_length >= 0) 192 | { 193 | c->recv_status = Client::BODY; 194 | c->_concat_body = std::string(c->_buffermalloc); 195 | size_t pos = c->_concat_body.find("\r\n\r\n"); 196 | c->_concat_body.erase(0, pos + 4); 197 | LOG_WRT(Logger::DEBUG, "c->_concat_body = " + c->_concat_body); 198 | memset(c->_buffermalloc, '\0', RECV_BUFFER + 1); 199 | } 200 | else 201 | c->recv_status = Client::COMPLETE; 202 | } 203 | else if (bytes >= RECV_BUFFER) 204 | { 205 | LOG_WRT(Logger::ERROR, "bytes >= RECV_BUFFER: Not a valid http request"); 206 | return (RET_ERROR); 207 | } 208 | } 209 | if (c->recv_status == Client::BODY) 210 | { 211 | LOG_WRT(Logger::DEBUG, "c->recv_status == BODY"); 212 | c->_request.update_body(); 213 | } 214 | if (c->recv_status == Client::COMPLETE) 215 | { 216 | LOG_WRT(Logger::DEBUG, "c->recv_status == COMPLETE"); 217 | LOG_WRT(Logger::DEBUG, "RAW REQUEST (" + std::to_string(bytes) + "):\n---\n" + std::string(c->_request._buffer) + "---"); 218 | FD_SET(c->_accept_fd, &g_conf._save_writefds); 219 | c->_request.display(); 220 | c->_request._buffer.clear(); 221 | c->_concat_body.clear(); 222 | } 223 | if (c->recv_status == Client::ERROR) 224 | { 225 | LOG_WRT(Logger::DEBUG, "c->recv_status == ERROR"); 226 | c->_is_connected = false; 227 | return (RET_ERROR); 228 | } 229 | } 230 | return (RET_SUCCESS); 231 | } 232 | 233 | int Server::sendResponse(Client *c) 234 | { 235 | int ret = 0; 236 | errno = 0; 237 | 238 | if (c->_response.send_status == c->_response.HANDLE_RESPONSE) 239 | { 240 | c->_response.handle_response(&(c->_request)); 241 | if (c->_wfd != -1 || c->_rfd != -1) 242 | return (1); 243 | c->_response.format_to_send(&(c->_request)); 244 | if (!c->_request._body_file.empty()) 245 | { 246 | if ((c->_response.read_fd = open(c->_request._body_file.c_str(), O_RDONLY|O_NONBLOCK)) < 0) 247 | return (-1); 248 | FD_SET(c->_response.read_fd, &g_conf._save_readfds); 249 | g_conf.add_fd(c->_response.read_fd); 250 | } 251 | FD_CLR(c->_accept_fd, &g_conf._save_readfds); 252 | c->_response.send_status = c->_response.SENDING; 253 | } 254 | 255 | if ((ret = send(c->_accept_fd, 256 | c->_response._to_send.c_str() + c->_response._bytes_send, 257 | c->_response._to_send.length() - c->_response._bytes_send, 258 | 0)) == -1) 259 | { 260 | LOG_WRT(Logger::ERROR, "1) Server::sendResponse -> send(): " + std::string(strerror(errno))); 261 | c->_is_connected = false; 262 | return (0); 263 | } 264 | else if (ret >= 0) 265 | { 266 | c->_last_active_time = utils_tmp::get_date(); 267 | c->_response._bytes_send += ret; 268 | LOG_WRT(Logger::DEBUG, "2) " + _name + "(" + std::to_string(_port) + ") -> client " + std::to_string(c->_accept_fd) 269 | + " | send = OK | ret = " + std::to_string(ret)); 270 | 271 | if (ret == 0 || c->_response._bytes_send >= c->_response._to_send.length()) 272 | { 273 | LOG_WRT(Logger::DEBUG, "3) sendResponse: c->_response._bytes_send=" + std::to_string(c->_response._bytes_send) 274 | + " >= _to_send.length()=" + std::to_string(c->_response._to_send.length())); 275 | 276 | c->_response._bytes_send = 0; 277 | c->_response._to_send.clear(); 278 | 279 | if (!c->_request._body_file.empty()) 280 | c->_rfd = c->_response.read_fd; 281 | else 282 | c->_is_finished = true; 283 | } 284 | else 285 | LOG_WRT(Logger::DEBUG, "4) sendResponse(): _bytes_send="+ std::to_string(c->_response._bytes_send) 286 | + " < _to_send.length()=" + std::to_string(c->_response._to_send.length()) 287 | + " -> keep going send()"); 288 | } 289 | return (1); 290 | } 291 | 292 | int Server::handleClientRequest(Client *c) 293 | { 294 | int ok_read = 0; 295 | int ok_write = 0; 296 | 297 | LOG_WRT(Logger::DEBUG, "Server::handleClientRequest() of client " + std::to_string(c->_accept_fd)); 298 | 299 | if (c->_accept_fd == -1) 300 | return (ok_read || ok_write); 301 | 302 | if (FD_ISSET(c->_accept_fd, &g_conf._readfds)) 303 | { 304 | LOG_WRT(Logger::INFO, "reading request of client " + std::to_string(c->_accept_fd)); 305 | if (!recvRequest(c)) 306 | return (0); 307 | else 308 | ok_read = 1; 309 | } 310 | else 311 | LOG_WRT(Logger::DEBUG, "reading not set for client " + std::to_string(c->_accept_fd)); 312 | 313 | if (FD_ISSET(c->_accept_fd, &g_conf._writefds)) 314 | { 315 | if (c->recv_status != Client::COMPLETE) 316 | { 317 | c->recv_status = Client::COMPLETE; 318 | LOG_WRT(Logger::DEBUG, "Server::handleClientRequest() passing recv_status to COMPLETE for client " + std::to_string(c->_accept_fd)); 319 | } 320 | LOG_WRT(Logger::INFO, "sending response to client " + std::to_string(c->_accept_fd)); 321 | if (!sendResponse(c)) 322 | return (0); 323 | else 324 | ok_write = 1; 325 | } 326 | else 327 | LOG_WRT(Logger::DEBUG, "writing not set for client " + std::to_string(c->_accept_fd)); 328 | return (ok_read || ok_write); 329 | } 330 | -------------------------------------------------------------------------------- /project/v1/srcs/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Headers.hpp" 2 | 3 | std::string get_last_modif(std::string file) 4 | { 5 | struct stat info; 6 | std::string sec; 7 | struct tm time; 8 | std::string date; 9 | char buffer[1000]; 10 | 11 | stat(file.c_str(), &info); 12 | sec = std::to_string(info.st_mtime).c_str(); 13 | strptime(sec.c_str(), "%s", &time); 14 | strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S CEST", &time); 15 | date = buffer; 16 | return (date); 17 | } 18 | 19 | std::string get_date(void) 20 | { 21 | struct timeval tv; 22 | struct tm time; 23 | struct timezone tz; 24 | char buffer[1000]; 25 | std::string date; 26 | 27 | gettimeofday(&tv, &tz); 28 | strptime(std::to_string(tv.tv_sec).c_str(), "%s", &time); 29 | strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S CEST", &time); 30 | date = buffer; 31 | return (date); 32 | } 33 | 34 | std::string get_content_type(std::string file) 35 | { 36 | std::string temp; 37 | int j = 0; 38 | int i = file.size() - 1; 39 | std::string ext[67] = {"php", "aac", "abw", "arc", "avi", "azw", "bin", "bz", "bz2", "csh", "css", "csv", "doc", "docsx", "eot", "epub", "gif", "htm", "html", "ico", 40 | "ics", "jar", "jpeg", "jpg", "js", "json", "mid", "midi", "mpeg", "mpkg", "odp", "ods", "odt", "oga", "ogv", "ogx", "otf", "png", "pdf", "ppt", "pptx", "rar", "rtf", "sh" 41 | "svg", "swf", "tar", "tif", "tiff", "ts", "ttf", "vsd", "wav", "weba", "webm", "webp", "woff" ,"woff2", "xhtml", "xls", "xlsx","xml", "xul", "zip", "3gp", "3g2", "7z"}; 42 | std::string ret[67] = {"text/html", "audio/aac", "application/x-abiword", "application/octet-stream", "video/x-msvideo", "application/vnd.amazon.ebook", "application/octet-stream", "application/x-bzip", 43 | "application/x-bzip2", "application/x-csh", "text/css", "text/csv", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-fontobject", 44 | "application/epub+zip", "image/gif", "text/html", "text/html", "image/x-icon", "text/calendar", "application/java-archive", "image/jpeg", "image/jpeg" ,"application/javascript", "application/json", 45 | "audio/midi","audio/midi", "video/mpeg", " application/vnd.apple.installer+xml", "application/vnd.oasis.opendocument.presentation", "application/vnd.oasis.opendocument.spreadsheet", "application/vnd.oasis.opendocument.text", "audio/ogg", "video/ogg", "application/ogg", "font/otf", "image/png", "application/pdf", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", 46 | "application/x-rar-compressed", "application/rtf", "application/x-sh", "image/svg+xml", "application/x-shockwave-flash", "application/x-tar", "image/tiff", "image/tiff", "application/typescript", "font/ttf", "application/vnd.visio", "audio/x-wav", "audio/webm", "video/webm", "image/webp", "font/woff", "font/woff2", "application/xhtml+xml", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 47 | "application/xml", "application/vnd.mozilla.xul+xml", "application/zip", "video/3gpp", "video/3gpp2", "application/x-7z-compressed"}; 48 | 49 | while (file[i] != '.') 50 | i--; 51 | temp = file.substr(i + 1, file.size()); 52 | while (j < 67) 53 | { 54 | if (ext[j] == temp) 55 | return (ret[j]); 56 | j++; 57 | } 58 | return ("text/plain"); 59 | } 60 | 61 | std::string get_location_header(Request *req) 62 | { 63 | int i = req->_file.size() - 1; 64 | int j = req->_location->_uri.size() - 1; 65 | std::string temp; 66 | while (req->_file[i] != '/') 67 | i--; 68 | if (req->_location->_uri[j] == '/') 69 | temp = req->_location->_uri + req->_file.substr(i + 1, req->_file.size()); 70 | else 71 | temp = req->_location->_uri + "/" + req->_file.substr(i + 1, req->_file.size()); 72 | return (temp); 73 | } 74 | 75 | int set_laguage(Request *req) 76 | { 77 | size_t i = 0; 78 | size_t j; 79 | std::string temp; 80 | 81 | while (i < req->_accept_language.size()) 82 | { 83 | if (req->_accept_language[i] != "") 84 | { 85 | temp = req->_accept_language[i]; 86 | if ((j = temp.find(';')) != std::string::npos) 87 | temp = temp.substr(0, i); 88 | req->_file = req->_file + "." + temp; 89 | std::ifstream file(req->_file); 90 | if (file.good()) 91 | { 92 | file.close(); 93 | return (1); 94 | } 95 | else 96 | unset_extension(req); 97 | } 98 | i++; 99 | } 100 | return (0); 101 | } 102 | 103 | int set_charset(Request *req) 104 | { 105 | size_t i = 0; 106 | size_t j; 107 | std::string temp; 108 | 109 | while (i < req->_accept_charset.size()) 110 | { 111 | if (req->_accept_charset[i] != "") 112 | { 113 | temp = req->_accept_charset[i]; 114 | if ((j = temp.find(';')) != std::string::npos) 115 | temp = temp.substr(0, i); 116 | req->_file = req->_file + "." + temp; 117 | std::ifstream file(req->_file); 118 | if (file.good()) 119 | { 120 | file.close(); 121 | return (1); 122 | } 123 | else 124 | unset_extension(req); 125 | } 126 | i++; 127 | } 128 | return (0); 129 | } 130 | 131 | void unset_extension(Request *req) 132 | { 133 | int i = req->_file.size() - 1; 134 | while (req->_file[i] && req->_file[i] != '.') 135 | i--; 136 | req->_file = req->_file.substr(0, i); 137 | } 138 | 139 | std::string map_to_string(std::map m, char delim) 140 | { 141 | std::stringstream ret; 142 | size_t i = 0; 143 | 144 | ret.clear(); 145 | while (i < m.size()) 146 | { 147 | ret << m[int(i)]; 148 | if ((i + 1) < m.size()) 149 | ret << delim << " "; 150 | ++i; 151 | } 152 | return (ret.str()); 153 | } 154 | 155 | std::string vector_to_string(std::vector v, char delim) 156 | { 157 | std::stringstream ret; 158 | size_t i = 0; 159 | int delim_is_space = 0; 160 | 161 | if (delim_is_space == ' ') 162 | delim_is_space = 1; 163 | 164 | ret.clear(); 165 | while (i < v.size()) 166 | { 167 | ret << v[int(i)]; 168 | if ((i + 1) < v.size()) 169 | { 170 | ret << delim; 171 | if (!delim_is_space) 172 | ret << " "; 173 | } 174 | ++i; 175 | } 176 | return (ret.str()); 177 | } 178 | 179 | void displayMap(std::map map) 180 | { 181 | std::map::iterator it = map.begin(); 182 | int i = 0; 183 | 184 | while (it != map.end()) 185 | { 186 | std::cout << ++i << ") " << it->first << "=" << it->second << std::endl; 187 | ++it; 188 | } 189 | } 190 | 191 | int is_extension(std::string file, std::string ext) 192 | { 193 | int i = file.size() - 1; 194 | std::string temp; 195 | while (i > 0 && file[i] != '.') 196 | i--; 197 | if (i == 0) 198 | return (0); 199 | temp = file.substr(i, file.size() - 1); 200 | if (temp == ext) 201 | return (1); 202 | return (0); 203 | } 204 | 205 | int compare_date(std::string a, std::string b) 206 | { 207 | struct tm c; 208 | struct tm d; 209 | strptime(a.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &c); 210 | strptime(b.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &d); 211 | if (c.tm_year > d.tm_year) 212 | return (1); 213 | else if (c.tm_year == d.tm_year && c.tm_wday > d.tm_wday) 214 | return (1); 215 | else if (c.tm_year == d.tm_year && c.tm_wday == d.tm_wday && c.tm_hour > d.tm_hour) 216 | return (1); 217 | else if (c.tm_year == d.tm_year && c.tm_wday == d.tm_wday && c.tm_hour == d.tm_hour && c.tm_min > d.tm_min) 218 | return (1); 219 | else if (c.tm_year == d.tm_year && c.tm_wday == d.tm_wday && c.tm_hour == d.tm_hour && c.tm_min == d.tm_min && c.tm_sec >= d.tm_sec) 220 | return (1); 221 | return (0); 222 | } 223 | -------------------------------------------------------------------------------- /project/v1/srcs/get_next_line/get_next_line.cpp: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* ::: :::::::: */ 4 | /* get_next_line.cpp :+: :+: :+: */ 5 | /* +:+ +:+ +:+ */ 6 | /* By: froussel +#+ +:+ +#+ */ 7 | /* +#+#+#+#+#+ +#+ */ 8 | /* Created: 2019/10/12 14:53:17 by froussel #+# #+# */ 9 | /* Updated: 2020/09/12 17:07:16 by froussel ### ########.fr */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "get_next_line.hpp" 14 | 15 | static int free_all(char **str) 16 | { 17 | if (*str) 18 | { 19 | free(*str); 20 | *str = NULL; 21 | } 22 | return (-1); 23 | } 24 | 25 | static int is_line(char *str) 26 | { 27 | int i; 28 | 29 | i = -1; 30 | while (str[++i]) 31 | if (str[i] == '\n') 32 | return (i); 33 | return (-1); 34 | } 35 | 36 | static int give_line(char **str, char **line, int ret) 37 | { 38 | char *s; 39 | int len; 40 | 41 | s = NULL; 42 | if (!*str || !**str) 43 | { 44 | if (!(*line = ft_strdup("\0"))) 45 | return (free_all(str)); 46 | } 47 | else if ((len = is_line(*str)) >= 0) 48 | { 49 | if (!(*line = ft_substr(*str, 0, len))) 50 | return (free_all(str)); 51 | if (!(s = ft_substr(*str, len + 1, ft_strlen(*str)))) 52 | return (free_all(str)); 53 | ret = 1; 54 | } 55 | else 56 | { 57 | if (!(*line = ft_substr(*str, 0, ft_strlen(*str)))) 58 | return (free_all(str)); 59 | } 60 | free_all(str); 61 | *str = s; 62 | return (ret); 63 | } 64 | 65 | int get_next_line(int fd, char **line) 66 | { 67 | static char *str = NULL; 68 | char buff[BUFFER_SIZE + 1]; 69 | char *new_str; 70 | ssize_t i; 71 | 72 | if (!line || fd < 0) 73 | return (free_all(&str)); 74 | while ((i = read(fd, buff, BUFFER_SIZE)) > 0) 75 | { 76 | buff[i] = '\0'; 77 | if (!(new_str = ft_strjoin(str, buff))) 78 | return (free_all(&str)); 79 | free_all(&str); 80 | str = new_str; 81 | if (is_line(str) >= 0) 82 | break ; 83 | } 84 | if (i < 0) 85 | return (free_all(&str)); 86 | return (give_line(&str, line, 0)); 87 | } 88 | -------------------------------------------------------------------------------- /project/v1/srcs/get_next_line/get_next_line.hpp: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* ::: :::::::: */ 4 | /* get_next_line.hpp :+: :+: :+: */ 5 | /* +:+ +:+ +:+ */ 6 | /* By: froussel +#+ +:+ +#+ */ 7 | /* +#+#+#+#+#+ +#+ */ 8 | /* Created: 2019/10/12 14:54:47 by froussel #+# #+# */ 9 | /* Updated: 2020/09/27 16:18:26 by froussel ### ########.fr */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #ifndef GET_NEXT_LINE_H 14 | # define GET_NEXT_LINE_H 15 | 16 | # include "stdlib.h" 17 | # include "unistd.h" 18 | 19 | # define BUFFER_SIZE 65536 20 | 21 | int get_next_line(int fd, char **line); 22 | size_t ft_strlen(const char *s); 23 | char *ft_strjoin(char const *str1, char const *str2); 24 | char *ft_substr(char const *s, unsigned int start, size_t len); 25 | char *ft_strdup(const char *s1); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /project/v1/srcs/get_next_line/get_next_line_utils.cpp: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* ::: :::::::: */ 4 | /* get_next_line_utils.cpp :+: :+: :+: */ 5 | /* +:+ +:+ +:+ */ 6 | /* By: froussel +#+ +:+ +#+ */ 7 | /* +#+#+#+#+#+ +#+ */ 8 | /* Created: 2019/10/12 14:54:08 by froussel #+# #+# */ 9 | /* Updated: 2020/09/12 17:08:20 by froussel ### ########.fr */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "get_next_line.hpp" 14 | 15 | size_t ft_strlen(const char *s) 16 | { 17 | size_t i; 18 | 19 | if (!s) 20 | return (0); 21 | i = 0; 22 | while (s[i]) 23 | i++; 24 | return (i); 25 | } 26 | 27 | char *ft_strjoin(char const *str1, char const *str2) 28 | { 29 | char *res; 30 | char *beg; 31 | int len; 32 | 33 | if (!str1 && !str2) 34 | return (0); 35 | len = 0; 36 | if (str1) 37 | len = ft_strlen(str1); 38 | if (str2) 39 | len += ft_strlen(str2); 40 | if (!(res = static_cast(malloc(sizeof(*res) * (len + 1))))) 41 | return (0); 42 | beg = res; 43 | if (str1) 44 | while (*str1) 45 | *res++ = *str1++; 46 | if (str2) 47 | while (*str2) 48 | *res++ = *str2++; 49 | *res = '\0'; 50 | return (beg); 51 | } 52 | 53 | char *ft_substr(char const *s, unsigned int start, size_t len) 54 | { 55 | char *ps; 56 | size_t i; 57 | 58 | if (!s) 59 | return (NULL); 60 | if (start > ft_strlen(s)) 61 | len = 0; 62 | else 63 | len -= start; 64 | if (!(ps = static_cast(malloc(sizeof(*ps) * (len + 1))))) 65 | return (0); 66 | i = 0; 67 | while (len-- > 0 && s[start + i]) 68 | { 69 | ps[i] = s[start + i]; 70 | i++; 71 | } 72 | ps[i] = '\0'; 73 | return (ps); 74 | } 75 | 76 | char *ft_strdup(const char *s1) 77 | { 78 | size_t i; 79 | char *ps; 80 | 81 | i = 0; 82 | while (s1[i]) 83 | i++; 84 | if (!(ps = static_cast(malloc(sizeof(*ps) * (i + 1))))) 85 | return (0); 86 | i = 0; 87 | while (s1[i]) 88 | { 89 | ps[i] = s1[i]; 90 | i++; 91 | } 92 | ps[i] = '\0'; 93 | return (ps); 94 | } 95 | -------------------------------------------------------------------------------- /project/v1/srcs/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/Headers.hpp" 2 | 3 | Conf g_conf; 4 | 5 | void print_clients_of_all_servers(void) 6 | { 7 | Server *s; 8 | Client *c; 9 | LOG_WRT(Logger::DEBUG, "-------------------------------"); 10 | std::vector::iterator it_s = g_conf._servers.begin(); 11 | for (; it_s != g_conf._servers.end(); it_s++) 12 | { 13 | s = *it_s; 14 | LOG_WRT(Logger::DEBUG, "SERVER : " + s->_name); 15 | std::vector::iterator it_c = s->_clients.begin(); 16 | for (; it_c != s->_clients.end(); it_c++) 17 | { 18 | c = *it_c; 19 | LOG_WRT(Logger::DEBUG, "<==>CLIENT fd=" + std::to_string(c->_accept_fd)); 20 | } 21 | } 22 | LOG_WRT(Logger::DEBUG, "-------------------------------"); 23 | } 24 | 25 | void print_all_fd(void) 26 | { 27 | std::list::iterator it_fd = g_conf._active_fds.begin(); 28 | std::string fds; 29 | 30 | LOG_WRT(Logger::DEBUG, "LIST _active_fds (size=" + std::to_string(g_conf._active_fds.size()) + ")"); 31 | //g_conf._active_fds.sort(); 32 | for (; it_fd != g_conf._active_fds.end(); ++it_fd) 33 | { 34 | fds += std::to_string(*it_fd) + " | "; 35 | } 36 | fds += "\n"; 37 | LOG_WRT(Logger::DEBUG, fds); 38 | LOG_WRT(Logger::DEBUG, "-------------------------------"); 39 | } 40 | 41 | void print_save_fds(void) 42 | { 43 | std::list::iterator it_fd; 44 | int nb; 45 | 46 | it_fd = g_conf._active_fds.begin(); 47 | std::string saved_read_fds; 48 | nb = 0; 49 | 50 | for (; it_fd != g_conf._active_fds.end(); ++it_fd) 51 | { 52 | if (FD_ISSET(*it_fd, &g_conf._save_readfds)) 53 | { 54 | saved_read_fds += std::to_string(*it_fd) + ", "; 55 | nb++; 56 | } 57 | } 58 | saved_read_fds = "saved_read_fds (nb=" + std::to_string(nb) + "): " + saved_read_fds; 59 | LOG_WRT(Logger::DEBUG, std::string(CYAN_C) + saved_read_fds + std::string(RESET)); 60 | 61 | it_fd = g_conf._active_fds.begin(); 62 | std::string saved_write_fds; 63 | nb = 0; 64 | 65 | for (; it_fd != g_conf._active_fds.end(); ++it_fd) 66 | { 67 | if (FD_ISSET(*it_fd, &g_conf._save_writefds)) 68 | { 69 | saved_write_fds += std::to_string(*it_fd) + ", "; 70 | nb++; 71 | } 72 | } 73 | saved_write_fds = "saved_write_fds (nb=" + std::to_string(nb) + "): " + saved_write_fds; 74 | LOG_WRT(Logger::DEBUG, std::string(CYAN_C) + saved_write_fds + std::string(RESET)); 75 | 76 | LOG_WRT(Logger::DEBUG, "-------------------------------"); 77 | } 78 | 79 | void print_set_fds(void) 80 | { 81 | std::list::iterator it_fd; 82 | int nb; 83 | 84 | it_fd = g_conf._active_fds.begin(); 85 | std::string set_read_fds; 86 | nb = 0; 87 | 88 | for (; it_fd != g_conf._active_fds.end(); ++it_fd) 89 | { 90 | if (FD_ISSET(*it_fd, &g_conf._readfds)) 91 | { 92 | set_read_fds += std::to_string(*it_fd) + ", "; 93 | nb++; 94 | } 95 | } 96 | set_read_fds = "set_read_fds (nb=" + std::to_string(nb) + "): " + set_read_fds; 97 | LOG_WRT(Logger::DEBUG, std::string(MAGENTA_C) + set_read_fds + std::string(RESET)); 98 | 99 | it_fd = g_conf._active_fds.begin(); 100 | std::string set_write_fds; 101 | nb = 0; 102 | 103 | for (; it_fd != g_conf._active_fds.end(); ++it_fd) 104 | { 105 | if (FD_ISSET(*it_fd, &g_conf._writefds)) 106 | { 107 | set_write_fds += std::to_string(*it_fd) + ", "; 108 | nb++; 109 | } 110 | } 111 | set_write_fds = "set_write_fds (nb=" + std::to_string(nb) + "): " + set_write_fds; 112 | LOG_WRT(Logger::DEBUG, std::string(MAGENTA_C) + set_write_fds + std::string(RESET)); 113 | 114 | LOG_WRT(Logger::DEBUG, "-------------------------------"); 115 | } 116 | 117 | int erase_client_from_vector(Server *s, std::vector &v, std::vector::iterator &it_c) 118 | { 119 | Client *c = *it_c; 120 | delete c; 121 | it_c = v.erase(it_c); 122 | LOG_WRT(Logger::INFO, s->_name + " has now " + std::to_string(v.size()) + " client(s) connected"); 123 | if (v.empty()) 124 | return (1); 125 | else 126 | it_c = v.begin(); 127 | return (0); 128 | } 129 | 130 | 131 | void shutdown(int sig) 132 | { 133 | Server *s; 134 | std::vector::iterator it_s; 135 | Client *c; 136 | std::vector::iterator it_c; 137 | Location *l; 138 | std::vector::iterator it_l; 139 | 140 | g_conf._on = false; 141 | LOG_WRT(Logger::INFO, "\33[2K\r" + g_conf._webserv + " deleting clients ..."); 142 | 143 | for (it_s = g_conf._servers.begin(); it_s != g_conf._servers.end(); ++it_s) 144 | { 145 | s = *it_s; 146 | for (it_c = s->_clients.begin(); it_c!= s->_clients.end(); ++it_c) 147 | delete *it_c; 148 | for (it_c = s->_clients_503.begin(); it_c!= s->_clients_503.end(); ++it_c) 149 | delete *it_c; 150 | for (it_l = s->_locations.begin(); it_l!= s->_locations.end(); ++it_l) 151 | delete *it_l; 152 | } 153 | for (it_s = g_conf._servers.begin(); it_s != g_conf._servers.end(); ++it_s) 154 | delete *it_s; 155 | g_conf._servers.clear(); 156 | 157 | g_conf._servers.clear(); 158 | 159 | LOG_WRT(Logger::INFO, "\33[2K\r" + g_conf._webserv + " server size " + std::to_string(g_conf._servers.size())); 160 | print_clients_of_all_servers(); 161 | LOG_WRT(Logger::INFO, "\33[2K\r" + g_conf._webserv + " status off"); 162 | exit(EXIT_SUCCESS); 163 | } 164 | 165 | int main(int argc, char *argv[]) 166 | { 167 | Server *s; 168 | Client *c; 169 | std::vector::iterator it_c; 170 | int select_ret = 0; 171 | 172 | LOG_START(Logger::DEBUG, "", false); 173 | signal(SIGINT, shutdown); 174 | if (argc != 2 || !g_conf.parse(argv[1])) 175 | return (EXIT_ERROR); 176 | while (g_conf._on) 177 | { 178 | print_save_fds(); 179 | select_ret = g_conf.run_select(); 180 | print_set_fds(); 181 | if (select_ret == -1) 182 | { 183 | LOG_WRT(Logger::INFO, std::string(RED_C) + "break on select() == -1" + std::string(RESET)); 184 | break ; 185 | } 186 | else if (select_ret == 0) 187 | { 188 | LOG_WRT(Logger::INFO, std::string(RED_C) + "break on select() == 0" + std::string(RESET)); 189 | break ; 190 | } 191 | else 192 | { 193 | print_all_fd(); 194 | std::vector::iterator it_s = g_conf._servers.begin(); 195 | for (; it_s != g_conf._servers.end(); it_s++) 196 | { 197 | s = *it_s; 198 | LOG_WRT(Logger::DEBUG, "iterating over server:" + s->_name); 199 | // pour chaque serveur: 200 | // 1 - on accepte, s'il y en a une, la demande de connexion du client auprès du serveur it_s (FD_ISSET()) 201 | // 2 - on itère sur les clients_503 du serveur pour les servir 202 | // 3 - on itère sur les clients du serveur pour les servir 203 | 204 | // FD_ISSET(): check si le fd est dans le set (de ceux qui sont prêts à être lu, grâce au select). 205 | // the socket is "readable" if the remote peer (the client) closes it / select() returns if a read() will not block, not only when there's data available (meaning also if EOF) 206 | // (https://stackoverflow.com/questions/6453149/select-says-socket-is-ready-to-read-when-it-is-definitely-not-actually-is-close) 207 | if (FD_ISSET(s->_socket_fd, &g_conf._readfds)) 208 | { 209 | LOG_WRT(Logger::INFO, std::string(GREEN_C) + "new client on server " + s->_name + std::string(RESET)); 210 | s->acceptNewClient(); 211 | g_conf._nb_accepted_connections += 1; 212 | LOG_WRT(Logger::INFO, std::string(YELLOW_C) + "_nb_accepted_connections = " + std::to_string(g_conf._nb_accepted_connections) + std::string(RESET)); 213 | } 214 | 215 | for (it_c = s->_clients_503.begin(); it_c != s->_clients_503.end(); it_c++) 216 | { 217 | LOG_WRT(Logger::DEBUG, "iterating over client 503:" + std::to_string(c->_accept_fd)); 218 | exit(EXIT_FAILURE); 219 | c = *it_c; 220 | if (!c->_is_connected) 221 | { 222 | if (erase_client_from_vector(s, s->_clients_503, it_c)) 223 | break; 224 | else 225 | continue ; 226 | } 227 | if (!c->_is_finished) 228 | { 229 | if (c->_wfd != -1 && c->_read_ok == 1) 230 | { 231 | if (FD_ISSET(c->_wfd, &g_conf._writefds)) 232 | c->write_file(); 233 | } 234 | if (c->_rfd != -1) 235 | { 236 | if (FD_ISSET(c->_rfd, &g_conf._readfds)) 237 | c->read_file(c->_response._body); 238 | } 239 | if (c->_read_ok == 1) 240 | s->handleClientRequest(c); 241 | } 242 | if (c->_is_finished) 243 | c->reset(); 244 | if (utils_tmp::getSecondsDiff(c->_last_active_time) >= CLIENT_CONNECTION_TIMEOUT) 245 | { 246 | LOG_WRT(Logger::DEBUG, "client " 247 | + std::to_string(c->_accept_fd) 248 | + " TIMEOUT " 249 | + std::to_string(CLIENT_CONNECTION_TIMEOUT)); 250 | if (erase_client_from_vector(s, s->_clients_503, it_c)) 251 | break; 252 | else 253 | continue ; 254 | } 255 | } 256 | 257 | for (it_c = s->_clients.begin(); it_c != s->_clients.end(); it_c++) 258 | { 259 | c = *it_c; 260 | LOG_WRT(Logger::DEBUG, "iterating over existing client:" + std::to_string(c->_accept_fd)); 261 | if (!c->_is_connected) 262 | { 263 | if (erase_client_from_vector(s, s->_clients, it_c)) 264 | break; 265 | else 266 | continue ; 267 | } 268 | if (!c->_is_finished) 269 | { 270 | if (c->_wfd != -1 && c->_read_ok == 1) 271 | { 272 | if (FD_ISSET(c->_wfd, &g_conf._writefds)) 273 | c->write_file(); 274 | } 275 | if (c->_rfd != -1) 276 | { 277 | if (FD_ISSET(c->_rfd, &g_conf._readfds)) 278 | c->read_file(c->_response._body); 279 | } 280 | if (c->_read_ok == 1) 281 | s->handleClientRequest(c); 282 | } 283 | 284 | LOG_WRT(Logger::DEBUG, "client " + std::to_string(c->_accept_fd) 285 | + " secondsDiff = " + std::to_string(utils_tmp::getSecondsDiff(c->_last_active_time))); 286 | 287 | if (c->_is_finished) 288 | c->reset(); 289 | 290 | if (utils_tmp::getSecondsDiff(c->_last_active_time) >= CLIENT_CONNECTION_TIMEOUT) 291 | { 292 | LOG_WRT(Logger::DEBUG, "client " 293 | + std::to_string(c->_accept_fd) 294 | + " TIMEOUT " 295 | + std::to_string(CLIENT_CONNECTION_TIMEOUT)); 296 | if (erase_client_from_vector(s, s->_clients, it_c)) 297 | break; 298 | else 299 | continue ; 300 | } 301 | } 302 | } 303 | LOG_WRT(Logger::DEBUG, "---------------\n\n"); 304 | } 305 | } 306 | shutdown(1); 307 | return (EXIT_SUCCESS); 308 | } 309 | -------------------------------------------------------------------------------- /project/v1/srcs/methods/connect.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::connect(Request *req) 4 | { 5 | bad_request(req); 6 | } 7 | -------------------------------------------------------------------------------- /project/v1/srcs/methods/delete.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::ft_delete(Request *req) 4 | { 5 | int ret; 6 | std::ifstream file(req->_file); 7 | if (file.good()) 8 | { 9 | ret = remove(req->_file.c_str()); 10 | if (!ret) 11 | _status_code = OK_200; 12 | else 13 | _status_code = ACCEPTED_202; 14 | } 15 | else 16 | _status_code = NO_CONTENT_204; 17 | } -------------------------------------------------------------------------------- /project/v1/srcs/methods/get.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::get(Request *req) 4 | { 5 | int language = 0; 6 | int charset = 0; 7 | int ret = 0; 8 | 9 | LOG_WRT(Logger::DEBUG, "inside GET"); 10 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 11 | { 12 | if (_body != "") 13 | { 14 | _status_code = OK_200; 15 | _content_type[0] = "text/html"; 16 | return ; 17 | } 18 | language = set_laguage(req); 19 | charset = set_charset(req); 20 | } 21 | ret = utils_tmp::file_exists(req->_file.c_str()); 22 | // CGI 23 | if ((req->_method == "GET" 24 | && ((req->_location->_cgi_root != "" 25 | && is_extension(req->_file, ".cgi")) 26 | || (req->_location->_php_root != "" 27 | && is_extension(req->_file, ".php")))) 28 | && ret) 29 | { 30 | LOG_WRT(Logger::DEBUG, "CGI"); 31 | ft_cgi(req); 32 | req->_body_file = "./www/temp_file"; 33 | req->_is_body_file_header = true; 34 | _status_code = OK_200; 35 | _content_type[0] = "text/html"; 36 | _last_modified = get_last_modif(req->_file); 37 | } 38 | // Pas de CGI 39 | else if (ret) 40 | { 41 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 42 | { 43 | req->_client->_rfd = open(req->_file.c_str(), O_RDONLY); 44 | FD_SET(req->_client->_rfd, &g_conf._save_readfds); 45 | g_conf.add_fd(req->_client->_rfd); 46 | _last_modified = get_last_modif(req->_file); 47 | _status_code = OK_200; 48 | } 49 | else 50 | { 51 | FD_CLR(req->_client->_rfd, &g_conf._save_readfds); 52 | g_conf.remove_fd(req->_client->_rfd); 53 | close(req->_client->_rfd); 54 | req->_client->_rfd = -1; 55 | } 56 | } 57 | else 58 | { 59 | not_found(req); 60 | } 61 | if (req->_client->_read_ok == 1) 62 | { 63 | if (charset) 64 | unset_extension(req); 65 | if (language) 66 | unset_extension(req); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /project/v1/srcs/methods/option.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::option(Request *req) 4 | { 5 | std::string buffer; 6 | 7 | for (std::size_t i = 0; i < (req->_location->_method).size(); ++i) 8 | { 9 | buffer = buffer + (req->_location->_method)[i]; 10 | if (i != (req->_location->_method).size() - 1) 11 | buffer = buffer + ", "; 12 | } 13 | _status_code = OK_200; 14 | _allow = buffer; 15 | } -------------------------------------------------------------------------------- /project/v1/srcs/methods/post.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::post(Request *req) 4 | { 5 | int fd; 6 | int ret; 7 | 8 | if (((req->_location->_cgi_root != "" && is_extension(req->_file, req->_location->_cgi)) 9 | || (req->_location->_php_root != "" && is_extension(req->_file, "php")))) 10 | { 11 | if (req->_client->_wfd == -1 12 | && req->_client->_rfd == -1) 13 | { 14 | ft_cgi(req); 15 | _status_code = OK_200; 16 | _content_type[0] = "text/html"; 17 | 18 | req->_body_file = "./www/temp_file"; 19 | req->_is_body_file_header = true; 20 | } 21 | else 22 | { 23 | FD_CLR(req->_client->_wfd, &g_conf._save_writefds); 24 | g_conf.remove_fd(req->_client->_wfd); 25 | close(req->_client->_wfd); 26 | req->_client->_wfd = -1; 27 | } 28 | } 29 | else 30 | { 31 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 32 | { 33 | if (utils_tmp::file_exists(req->_file.c_str())) 34 | { 35 | if ((req->_client->_wfd = open(req->_file.c_str(), O_APPEND|O_WRONLY|O_NONBLOCK, 0666)) == -1) 36 | { 37 | _status_code = INTERNAL_ERROR_500; 38 | return ; 39 | } 40 | _status_code = OK_200; 41 | _body = "Ressource updated"; 42 | } 43 | else 44 | { 45 | if ((req->_client->_wfd = open(req->_file.c_str(), O_CREAT|O_APPEND|O_WRONLY|O_NONBLOCK, 0666)) == -1) 46 | { 47 | _status_code = INTERNAL_ERROR_500; 48 | return ; 49 | } 50 | _status_code = CREATED_201; 51 | _body = "Ressource created"; 52 | } 53 | FD_SET(req->_client->_wfd, &g_conf._save_writefds); 54 | g_conf.add_fd(req->_client->_wfd); 55 | } 56 | else 57 | { 58 | LOG_WRT(Logger::DEBUG, "req->_text_body(" + std::to_string(req->_text_body.length()) + ")= " + req->_text_body); 59 | FD_CLR(req->_client->_wfd, &g_conf._save_writefds); 60 | g_conf.remove_fd(req->_client->_wfd); 61 | close(req->_client->_wfd); 62 | req->_client->_wfd = -1; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /project/v1/srcs/methods/put.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::put(Request *req) 4 | { 5 | LOG_WRT(Logger::DEBUG, "inside put"); 6 | int ret = 0; 7 | 8 | if (req->_client->_wfd == -1 && req->_client->_rfd == -1) 9 | { 10 | ret = utils_tmp::file_exists(req->_file.c_str()); 11 | if (ret) 12 | { 13 | _status_code = OK_200; 14 | LOG_WRT(Logger::DEBUG, "put: file exists, is good\n"); 15 | } 16 | else 17 | { 18 | req->_client->_wfd = open(req->_file.c_str(), O_CREAT|O_WRONLY|O_NONBLOCK, 0666); 19 | LOG_WRT(Logger::DEBUG, "put: req->_client->_wfd=" + std::to_string(req->_client->_wfd)); 20 | if (req->_client->_wfd == -1) 21 | { 22 | not_found(req); 23 | return ; 24 | } 25 | _status_code = CREATED_201; 26 | _body = "Ressource created"; 27 | LOG_WRT(Logger::DEBUG, "put: file created\n"); 28 | close(req->_client->_wfd); 29 | } 30 | 31 | ret = utils_tmp::file_exists(req->_file.c_str()); 32 | if (ret) 33 | { 34 | req->_client->_wfd = open(req->_file.c_str(), O_WRONLY|O_NONBLOCK, 0666); 35 | FD_SET(req->_client->_wfd, &g_conf._save_writefds); 36 | g_conf.add_fd(req->_client->_wfd); 37 | LOG_WRT(Logger::DEBUG, "put: writing req->text_body (len=" + std::to_string(req->_text_body.length()) + ") inside _file\n"); 38 | } 39 | else 40 | { 41 | LOG_WRT(Logger::DEBUG, "put: failed to write body inside _file: \"" + std::string(strerror(errno)) + "\" -> 404\n"); 42 | not_found(req); 43 | } 44 | } 45 | else 46 | { 47 | LOG_WRT(Logger::DEBUG, "end of put\n"); 48 | FD_CLR(req->_client->_wfd, &g_conf._save_writefds); 49 | g_conf.remove_fd(req->_client->_wfd); 50 | close(req->_client->_wfd); 51 | req->_client->_wfd = -1; 52 | } 53 | } -------------------------------------------------------------------------------- /project/v1/srcs/methods/trace.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | void Response::trace(Request *req) 4 | { 5 | (void)req; 6 | int i; 7 | if (!_status_code) 8 | { 9 | _http_version = "HTTP/1.1"; 10 | _server = g_conf._webserv; 11 | _status_code = OK_200; 12 | _reason_phrase = code_to_reason[_status_code]; 13 | _content_type[0] = "message/http"; 14 | _date = get_date(); 15 | concat_to_send(); 16 | i = _to_send.size() - 3; 17 | _to_send[i] = '\0'; 18 | _to_send[i + 1] = '\0'; 19 | _to_send[i + 2] = '\0'; 20 | _body = _to_send; 21 | } 22 | } -------------------------------------------------------------------------------- /project/v1/srcs/request/Debug.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | /* 4 | ** Display in logs 5 | */ 6 | 7 | void Request::display(void) 8 | { 9 | size_t i; 10 | std::stringstream ss1; 11 | 12 | ss1 << "PARSED REQUEST:" << std::endl; 13 | 14 | ss1 << " 1) _method " << _method << std::endl; // 1 15 | ss1 << " 2) _uri " << _uri << std::endl; // 2 16 | ss1 << " 3) _http_version " << _http_version << std::endl; // 3 17 | ss1 << " 4) _accept_charset: "; // 4 18 | utils_tmp::print_map(ss1, _accept_charset); 19 | ss1 << " 5) _accept_language: "; // 5 20 | utils_tmp::print_map(ss1, _accept_language); 21 | ss1 << " 6) _authorization " << _authorization << std::endl; // 6 22 | ss1 << " 7) _content_language "; // 7 23 | utils_tmp::print_map(ss1, _content_language); 24 | ss1 << " 8) _content_length " << _content_length << std::endl; // 8 25 | ss1 << " 9) _content_location " << _content_location << std::endl; // 9 26 | ss1 << "10) _content_type " << _content_type << std::endl; // 10 27 | ss1 << "11) _date " << _date << std::endl; // 11 28 | ss1 << "12) _host " << _host << std::endl; // 12 29 | ss1 << "13) _referer " << _referer << std::endl; // 13 30 | ss1 << "14) _user_agent " << _user_agent << std::endl; // 14 31 | ss1 << "15) _text_body length " << std::to_string(_text_body.length()) /*: " << _text_body */<< std::endl; // 14 32 | ss1 << "16) _file " << _file << std::endl; 33 | ss1 << "17) _transfer_encoding " << _transfer_encoding << std::endl; 34 | ss1 << "18) _keep_alive " << _keep_alive << std::endl; 35 | LOG_WRT(Logger::INFO, ss1.str()); 36 | } 37 | -------------------------------------------------------------------------------- /project/v1/srcs/request/Parsing.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | /* 4 | ** 1. Request line 5 | */ 6 | 7 | int Request::parse_request_line() 8 | { 9 | std::string line; 10 | std::vector tokens; 11 | 12 | while (1) 13 | { 14 | line.clear(); 15 | utils_tmp::ft_getline(_buffer, line); 16 | tokens = utils_tmp::split(line, ' '); 17 | if (tokens.size() == 3) 18 | break ; 19 | } 20 | LOG_WRT(Logger::DEBUG, "Request::parse_request_line() = " + line); 21 | 22 | _method = tokens[0]; 23 | _uri = tokens[1]; 24 | _http_version = tokens[2]; 25 | return (1); 26 | } 27 | 28 | /* 29 | ** 2. Headers 30 | */ 31 | 32 | int Request::parse_headers() 33 | { 34 | std::string line; 35 | std::size_t pos; 36 | std::string key; 37 | std::string value; 38 | 39 | // headers 40 | while (!_buffer.empty()) 41 | { 42 | utils_tmp::ft_getline(_buffer, line); 43 | // parse key/value before/after first ':' 44 | pos = line.find(":"); 45 | if (pos == std::string::npos) 46 | break ; 47 | key = utils_tmp::trim(line.substr(0, pos)); 48 | utils_tmp::remove_return(key); 49 | value = utils_tmp::trim(line.substr(pos + 1)); 50 | utils_tmp::remove_return(value); 51 | if (key.empty()) 52 | break ; 53 | // fill corresponding request member variable with value 54 | fill_request(key, value); 55 | } 56 | return (1); 57 | } 58 | 59 | void Request::fill_request(std::string key, std::string value) 60 | { 61 | size_t i = 0; 62 | std::vector tokens; 63 | 64 | if (key == "Accept-Charset") 65 | { 66 | tokens = utils_tmp::split(value, ','); 67 | if (!tokens.empty()) 68 | { 69 | while (i < tokens.size()) 70 | { 71 | _accept_charset[i] = tokens[i]; 72 | i++; 73 | } 74 | } 75 | } 76 | else if (key == "Accept-Language") 77 | { 78 | tokens = utils_tmp::split(value, ','); 79 | if (!tokens.empty()) 80 | { 81 | while (i < tokens.size()) 82 | { 83 | _accept_language[i] = tokens[i]; 84 | i++; 85 | } 86 | } 87 | } 88 | else if (key == "Authorization") 89 | _authorization = value; 90 | else if (key == "Content-Language") 91 | { 92 | tokens = utils_tmp::split(value, ','); 93 | if (!tokens.empty()) 94 | { 95 | while (i < tokens.size()) 96 | { 97 | _content_language[i] = tokens[i]; 98 | i++; 99 | } 100 | } 101 | } 102 | else if (key == "Content-Length") // 8 103 | _content_length = std::stoi(value); 104 | else if (key == "Content-Location") // 9 105 | _content_location = value; 106 | else if (key == "Content-Type") // 10 107 | _content_type = value; 108 | else if (key == "Date") // 11 109 | _date = value; 110 | else if (key == "Host") // 12 111 | _host = value; 112 | else if (key == "Referer") // 13 113 | _referer = value; 114 | else if (key == "User-Agent") // 14 115 | _user_agent = value; 116 | else if (key == "Transfer-Encoding") // 17 117 | _transfer_encoding = value; 118 | else if (key == "X-Secret-Header-For-Test") 119 | _secret_header = value; 120 | else if (key == "Keep-Alive") 121 | _keep_alive = value; 122 | else if (key == "Connection") 123 | ; 124 | else if (key == "Accept-Encoding") 125 | ; 126 | else if (key == "Accept") 127 | ; 128 | } 129 | 130 | /* 131 | ** 3. Filename 132 | */ 133 | 134 | void Request::create_autoindex() 135 | { 136 | /* 137 | ** Definir le Path du fichier a traiter dans la reponse 138 | ** a partir de l'uri present dans la requete, et de la location 139 | */ 140 | 141 | DIR *dir; 142 | struct dirent *dent; 143 | 144 | dir = opendir(_file.c_str()); 145 | if (dir) 146 | { 147 | _client->_response._body.append("

"); 148 | while ((dent = readdir(dir)) != NULL) 149 | { 150 | if (std::string(dent->d_name) != "..") 151 | { 152 | _client->_response._body.append(dent->d_name); 153 | _client->_response._body.append("\n"); 154 | } 155 | } 156 | closedir(dir); 157 | _client->_response._body.append("

"); 158 | } 159 | std::cout << _client->_response._body; 160 | } 161 | 162 | int Request::get_location(std::string *uri, std::vector locations) 163 | { 164 | /* 165 | ** Recuperer la location presente dans le fichier de config 166 | ** a partir de l'uri present dans la requete 167 | */ 168 | int j; 169 | std::string uri_tmp; 170 | 171 | for (std::size_t i = 0; i < locations.size(); ++i) 172 | { 173 | if (locations[i]->_uri == *uri) 174 | { 175 | _location = locations[i]; 176 | return (1); 177 | } 178 | } 179 | j = (*uri).size() - 1; 180 | uri_tmp = *uri; 181 | while (uri_tmp.size() > 0) 182 | { 183 | while (uri_tmp[j] != '/' && j != 0) 184 | j--; 185 | uri_tmp = uri_tmp.substr(0, j); 186 | if (uri_tmp == "") 187 | uri_tmp = "/"; 188 | for (std::size_t i = 0; i < locations.size(); ++i) 189 | { 190 | if (locations[i]->_uri == uri_tmp) 191 | { 192 | _file = (*uri).substr(j + 1, (*uri).size()); 193 | _file_name = (*uri).substr(j + 1, (*uri).size()); 194 | _location = locations[i]; 195 | return (1); 196 | } 197 | } 198 | } 199 | return (0); 200 | } 201 | 202 | int Request::parse_filename(std::vector locations) 203 | { 204 | struct stat info; 205 | int i; 206 | 207 | 208 | parse_query_string(); 209 | get_location(&_uri, locations); 210 | 211 | if (_location) 212 | { 213 | i = (_location->_root).size() - 1; 214 | if ((_location->_root)[i] == '/') 215 | _file = _location->_root + _file; 216 | else 217 | _file = _location->_root + "/" + _file; 218 | if (stat(_file.c_str(), &info) == 0) 219 | { 220 | if (S_ISDIR(info.st_mode)) 221 | { 222 | if (_location->_autoindex == 1 && _method == "GET") 223 | create_autoindex(); 224 | else 225 | { 226 | i = _file.size() - 1; 227 | if (_file[i] == '/') 228 | _file = _file + _location->_index; 229 | else 230 | _file = _file + "/" + _location->_index; 231 | } 232 | } 233 | } 234 | } 235 | return (1); 236 | } 237 | 238 | /* 239 | ** 1. Request line + 2. Headers + 3. Filename 240 | */ 241 | 242 | int Request::parse(std::vector location) 243 | { 244 | LOG_WRT(Logger::DEBUG, "Request::parse()"); 245 | 246 | parse_request_line(); 247 | parse_filename(location); 248 | parse_headers(); 249 | 250 | return (RET_SUCCESS); 251 | } 252 | 253 | /* 254 | ** ---------------------------------------------------------------------------- 255 | */ 256 | 257 | /* 258 | ** Body as normal 259 | */ 260 | 261 | void Request::parse_body_length() 262 | { 263 | char *buff = _client->_buffermalloc; 264 | std::string &body = _client->_request._text_body; 265 | size_t cut; 266 | 267 | _client->_concat_body.append(buff); 268 | if (_content_length < 0) 269 | { 270 | _client->recv_status = Client::ERROR; 271 | return ; 272 | } 273 | size_t new_body_size = body.length() + _client->_concat_body.length(); 274 | if (new_body_size >= _content_length) 275 | { 276 | cut = _content_length - body.length(); 277 | body.append(_client->_concat_body.substr(0, cut)); 278 | LOG_WRT(Logger::DEBUG, "Request::parse_body_length(): Client::COMPLETE"); 279 | _client->recv_status = Client::COMPLETE; 280 | memset(buff, '\0', RECV_BUFFER + 1); 281 | } 282 | else 283 | { 284 | body.append(_client->_concat_body); 285 | memset(buff, '\0', RECV_BUFFER + 1); 286 | } 287 | } 288 | 289 | /* 290 | ** Body as chunked 291 | */ 292 | 293 | void Request::parse_body_chunked() 294 | { 295 | LOG_WRT(Logger::DEBUG, "start parse_body_chunked()"); 296 | 297 | char *recv_buffer = _client->_buffermalloc; 298 | std::string &body = _client->_request._text_body; 299 | size_t pos; 300 | 301 | _client->_concat_body.append(recv_buffer); 302 | 303 | while (true) 304 | { 305 | LOG_WRT(Logger::DEBUG, "--- start while ---"); 306 | LOG_WRT(Logger::DEBUG, "_client->_concat_body len = " + std::to_string(_client->_concat_body.length())); 307 | LOG_WRT(Logger::DEBUG, "_client->_line_size = " + std::to_string(_client->_line_size)); 308 | pos = _client->_concat_body.find("\r\n"); 309 | if (pos == std::string::npos) 310 | break ; 311 | else 312 | { 313 | // 1. get _client->_line_size 314 | if (_client->_line_size == -1) 315 | { 316 | std::string line = _client->_concat_body.substr(0, pos); 317 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): line = -" + line + "-"); 318 | _client->_line_size = utils_tmp::hexa_to_dec(line.c_str()); 319 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): _client->_line_size = " + std::to_string(_client->_line_size)); 320 | _client->_concat_body.erase(0, line.length() + 2); 321 | } 322 | // 2. get next line content for _client->_line_size first bytes 323 | if (_client->_line_size > 0) 324 | { 325 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): _client->_concat_body.length() = " + std::to_string(_client->_concat_body.length())); 326 | if (_client->_concat_body.length() >= _client->_line_size)// todo: if hexa is bigger than predict ? 327 | { 328 | pos = _client->_concat_body.find("\r\n"); 329 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): pos = " + std::to_string(pos)); 330 | if (pos == std::string::npos) 331 | break ; 332 | else if (_location->_max_body != 1 && pos > _location->_max_body) 333 | { 334 | _saved_error = REQUEST_ENTITY_TOO_LARGE_413; 335 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): REQUEST_ENTITY_TOO_LARGE_413"); 336 | } 337 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): substr(0, " + std::to_string(_client->_line_size) + ")"); 338 | body.append(_client->_concat_body.substr(0, _client->_line_size)); 339 | _client->_concat_body.erase(0, _client->_line_size + 2); 340 | _client->_line_size = -1; 341 | } 342 | else 343 | { 344 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): _client->_concat_body.length() < _client->_line_size"); 345 | break ; 346 | } 347 | } 348 | // 3. end chunked body parsing 349 | else if (_client->_line_size == 0) 350 | { 351 | LOG_WRT(Logger::DEBUG, "Request::parse_body_chunked(): Body fully parsed"); 352 | _client->recv_status = Client::COMPLETE; 353 | break ; 354 | } 355 | } 356 | LOG_WRT(Logger::DEBUG, "--- end while ---\n"); 357 | } 358 | memset(recv_buffer, '\0', RECV_BUFFER + 1); 359 | LOG_WRT(Logger::DEBUG, "end parse_body_chunked()"); 360 | } 361 | 362 | void Request::update_body() 363 | { 364 | if (_transfer_encoding == "chunked") 365 | { 366 | parse_body_chunked(); 367 | } 368 | else if (_content_length >= 0) 369 | { 370 | parse_body_length(); 371 | } 372 | else 373 | { 374 | LOG_WRT(Logger::DEBUG, "Request::update_body(): Client::COMPLETE"); 375 | _client->recv_status = Client::COMPLETE; 376 | } 377 | } 378 | 379 | /* 380 | ** ---------------------------------------------------------------------------- 381 | */ 382 | 383 | /* 384 | ** Query string 385 | */ 386 | 387 | void Request::parse_query_string() 388 | { 389 | int i = 0; 390 | std::string uri = _uri; 391 | 392 | while (uri[i] && uri[i] != '?') 393 | i++; 394 | if (uri[i] == '?') 395 | { 396 | _query = uri.substr(i + 1, uri.size()); 397 | _uri = uri.substr(0, i); 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /project/v1/srcs/request/Request.cpp: -------------------------------------------------------------------------------- 1 | #include "../../includes/Headers.hpp" 2 | 3 | /* 4 | ** Constructors / Destructors 5 | */ 6 | 7 | Request::Request(void) 8 | { 9 | init(); 10 | } 11 | 12 | void Request::init(void) 13 | { 14 | // Request Line 15 | _method.clear(); 16 | _uri.clear(); 17 | _http_version.clear(); 18 | _body_file = ""; 19 | // Request Headers, dans l'ordre du sujet 20 | _accept_charset.clear(); 21 | _accept_language.clear(); 22 | _authorization.clear(); 23 | _content_language.clear(); 24 | _content_length = -1; 25 | _content_location.clear(); 26 | _content_type.clear(); 27 | _date.clear(); 28 | _host.clear(); 29 | _referer.clear(); 30 | _user_agent.clear(); 31 | // Request body 32 | _text_body.clear(); 33 | _transfer_encoding.clear(); 34 | // Other headers not mentionned in the subject 35 | _keep_alive.clear(); 36 | 37 | // Autres variables qui ne sont pas des headers 38 | _body_length = -1; 39 | _body_type = -1; 40 | _saved_error = -1; 41 | _file.clear(); 42 | _location = NULL; 43 | _buffer.clear(); 44 | 45 | _secret_header.clear(); 46 | _location = NULL; 47 | } 48 | 49 | /* 50 | ** for CGI: 51 | */ 52 | 53 | std::map Request::headers_to_map(void) 54 | { 55 | std::map ret; 56 | 57 | /* 58 | ** Request Headers, dans l'ordre du sujet 59 | */ 60 | 61 | ret["Accept-Charset"] = map_to_string(_accept_charset, ';'); 62 | ret["Accept-Language"] = map_to_string(_accept_language, ','); 63 | ret["Authorization"] = _authorization; 64 | ret["Content-Language"] = map_to_string(_content_language, ','); 65 | ret["Content-Length"] = std::to_string(_content_length); 66 | ret["Content-Location"] = _content_location; 67 | ret["Content-Type"] = _content_type; 68 | ret["Date"] = _date; 69 | ret["Host"] = _host; 70 | ret["Referer"] = _referer; 71 | ret["User-Agent"] = _user_agent; 72 | if (!_secret_header.empty()) 73 | ret["X-Secret-Header-For-Test"] = _secret_header; 74 | 75 | return (ret); 76 | } 77 | 78 | /* 79 | ** Reset before waiting for new call 80 | */ 81 | 82 | void Request::reset(void) 83 | { 84 | init(); 85 | } 86 | -------------------------------------------------------------------------------- /project/v1/srcs/utils_tmp.cpp: -------------------------------------------------------------------------------- 1 | #include "../includes/utils_tmp.hpp" 2 | 3 | std::string utils_tmp::get_date(void) 4 | { 5 | struct timeval tv; 6 | struct tm time; 7 | struct timezone tz; 8 | char buffer[1000]; 9 | std::string date; 10 | 11 | gettimeofday(&tv, &tz); 12 | strptime(std::to_string(tv.tv_sec).c_str(), "%s", &time); 13 | strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S CEST", &time); 14 | date = buffer; 15 | return (date); 16 | } 17 | 18 | int utils_tmp::isspace(int c) 19 | { 20 | char space[] = "\t\n\v\f\r "; 21 | int i = -1; 22 | 23 | while (++i < 6) 24 | if ((char)c == space[i]) 25 | return (1); 26 | return (0); 27 | } 28 | 29 | std::vector utils_tmp::split_string(std::string &str, std::string set) 30 | { 31 | std::vector split; 32 | std::size_t p1; 33 | std::size_t p2 = 0; 34 | 35 | while (p2 < str.length()) 36 | { 37 | if ((p1 = str.find_first_not_of(set, p2)) == std::string::npos) 38 | return (split); 39 | if ((p2 = str.find_first_of(set, p1)) == std::string::npos) 40 | p2 = str.length(); 41 | split.push_back(str.substr(p1, (p2 - p1))); 42 | } 43 | return (split); 44 | } 45 | 46 | bool utils_tmp::file_exists(const char *filename) 47 | { 48 | struct stat buffer; 49 | 50 | return (stat (filename, &buffer) == 0); 51 | } 52 | 53 | int utils_tmp::get_buffer(std::string file, std::string &buff) 54 | { 55 | int fd; 56 | int ret; 57 | char buffer[BUFFER_SIZE + 1]; 58 | if ((fd = open(file.c_str(), O_RDONLY|O_NONBLOCK)) < 0) 59 | return (-1); 60 | while ((ret = read(fd, buffer, BUFFER_SIZE)) > 0) 61 | { 62 | buffer[ret] = '\0'; 63 | buff.append(buffer); 64 | } 65 | close(fd); 66 | return (ret); 67 | } 68 | 69 | int utils_tmp::extract_body(std::string &buff) 70 | { 71 | size_t pos; 72 | 73 | if ((pos = buff.find("\r\n\r\n")) == std::string::npos) 74 | return (-1); 75 | buff.erase(0, pos + 4); 76 | 77 | return (0); 78 | } 79 | 80 | int utils_tmp::hexa_to_dec(const char *hexVal) 81 | { 82 | int len = strlen(hexVal); 83 | int base = 1; 84 | int dec_val = 0; 85 | 86 | for (int i = len - 1; i >= 0; --i) 87 | { 88 | if (hexVal[i] >= '0' && hexVal[i] <= '9') 89 | { 90 | dec_val += (hexVal[i] - 48) * base; 91 | base = base * 16; 92 | } 93 | else if (hexVal[i] >= 'A' && hexVal[i] <= 'F') 94 | { 95 | dec_val += (hexVal[i] - 55)*base; 96 | base = base * 16; 97 | } 98 | else if (hexVal[i] >= 'a' && hexVal[i] <= 'f') 99 | { 100 | dec_val += (hexVal[i] - 87)*base; 101 | base = base * 16; 102 | } 103 | } 104 | return (dec_val); 105 | } 106 | 107 | 108 | void utils_tmp::remove_return(std::string &str) 109 | { 110 | size_t pos = str.find_last_of('\r'); 111 | 112 | if (pos != std::string::npos) 113 | str.erase(pos); 114 | } 115 | 116 | size_t utils_tmp::getSecondsDiff(std::string complete_time) 117 | { 118 | struct tm complete; 119 | struct tm *now; 120 | struct timeval now_timeval; 121 | size_t diff = 0; 122 | 123 | strptime(complete_time.c_str(), "%a, %d %b %Y %T", &complete); 124 | gettimeofday(&now_timeval, NULL); 125 | now = localtime(&now_timeval.tv_sec); 126 | diff = (now->tm_hour - complete.tm_hour) * 3600; 127 | diff += (now->tm_min - complete.tm_min) * 60; 128 | diff += (now->tm_sec - complete.tm_sec); 129 | return (diff); 130 | } 131 | 132 | void utils_tmp::ft_getline(std::string &b, std::string &line) 133 | { 134 | size_t pos; 135 | 136 | pos = b.find("\n"); 137 | 138 | if (pos != std::string::npos) 139 | { 140 | line = std::string (b, 0, pos++); 141 | b = b.substr(pos); 142 | } 143 | else 144 | { 145 | if (b[b.size() - 1] == '\n') 146 | b = b.substr(b.size()); 147 | else 148 | { 149 | line = b; 150 | b = b.substr(b.size()); 151 | } 152 | } 153 | } 154 | 155 | std::vector utils_tmp::split(const std::string& str, char delim) 156 | { 157 | std::vector strings; 158 | size_t start; 159 | size_t end = 0; 160 | while ((start = str.find_first_not_of(delim, end)) != std::string::npos) 161 | { 162 | end = str.find(delim, start); 163 | strings.push_back(str.substr(start, end - start)); 164 | } 165 | return strings; 166 | } 167 | 168 | std::string utils_tmp::trim(const std::string& str) 169 | { 170 | size_t first = str.find_first_not_of(' '); 171 | 172 | if (std::string::npos == first) 173 | return str; 174 | size_t last = str.find_last_not_of(' '); 175 | return (std::string(str.substr(first, (last - first + 1)))); 176 | } 177 | 178 | void utils_tmp::print_map(std::stringstream &ss1, std::map map) 179 | { 180 | size_t i = 0; 181 | 182 | if (map.empty()) 183 | ss1 << std::endl; 184 | else 185 | { 186 | while (i < map.size()) 187 | ss1 << " " << map[i++]; 188 | ss1 << std::endl; 189 | } 190 | } 191 | 192 | void utils_tmp::free_strtab(char ***tab) 193 | { 194 | size_t i = 0; 195 | while ((*tab)[i]) 196 | i++; 197 | size_t j = 0; 198 | while (j <= i) 199 | { 200 | free((*tab)[j++]); 201 | } 202 | free(*tab); 203 | tab = NULL; 204 | } 205 | 206 | int utils_tmp::is_valide_methods(std::string &meth) 207 | { 208 | if (meth == "GET" 209 | || meth == "HEAD" 210 | || meth == "PUT" 211 | || meth == "POST" 212 | || meth == "DELETE" 213 | || meth == "OPTIONS" 214 | || meth == "TRACE" 215 | || meth == "CONNECT") 216 | return (1); 217 | return (0); 218 | } 219 | 220 | std::string utils_tmp::dec_to_hex(long int dec) 221 | { 222 | char shex[20]; 223 | int i; 224 | std::string hexa; 225 | 226 | i = 0; 227 | while (dec) 228 | { 229 | shex[i++] = "0123456789ABCDEF"[dec % 16]; 230 | dec = dec / 16; 231 | } 232 | if (i == 0) 233 | shex[i++] = '0'; 234 | shex[i] = '\0'; 235 | hexa = shex; 236 | std::reverse(hexa.begin(), hexa.end()); 237 | return (hexa); 238 | } -------------------------------------------------------------------------------- /project/v1/tests/deleteTests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | import os 4 | import sys 5 | from printReqAndResp import * 6 | 7 | tests_to_run = [] 8 | ac = len(sys.argv) 9 | if ac > 1: 10 | for av in sys.argv[1:]: 11 | # print ("to run: test", av) 12 | tests_to_run.append(int(av)) 13 | 14 | 15 | i = 0 16 | 17 | # --- 18 | i += 1 19 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test delete: SUCCESS\n" + bcolors.ENDC) 20 | if len(tests_to_run) == 0 or i in tests_to_run: 21 | r = requests.delete('http://localhost:8081/delete/') 22 | printResponse(r, i) 23 | 24 | # --- 25 | i += 1 26 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test delete: no content\n" + bcolors.ENDC) 27 | if len(tests_to_run) == 0 or i in tests_to_run: 28 | r = requests.delete('http://localhost:8081/delete/no_content') 29 | printResponse(r, i) 30 | 31 | # DELETE ------------------------------------------------------------------------- 32 | -------------------------------------------------------------------------------- /project/v1/tests/getTests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | import os 4 | import sys 5 | 6 | tests_to_run = [] 7 | ac = len(sys.argv) 8 | if ac > 1: 9 | for av in sys.argv[1:]: 10 | # print ("to run: test", av) 11 | tests_to_run.append(int(av)) 12 | 13 | from printReqAndResp import * 14 | 15 | i = 0 16 | 17 | # --- 18 | i += 1 19 | print (bcolors.OKBLUE + "\n" + str(i) + ". BASIC Test Get\n" + bcolors.ENDC) 20 | if len(tests_to_run) == 0 or i in tests_to_run: 21 | r = requests.get('http://localhost:8081/') 22 | printResponse(r, i) 23 | 24 | # --- 25 | i += 1 26 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get with Accept-Language header: fr \n" + bcolors.ENDC) 27 | headers = {'Accept-Language': 'fr'} 28 | if len(tests_to_run) == 0 or i in tests_to_run: 29 | r = requests.get('http://localhost:8081/test', headers=headers) 30 | printResponse(r, i) 31 | 32 | # --- 33 | i += 1 34 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get with Accept-Language header: en \n" + bcolors.ENDC) 35 | headers = {'Accept-Language': 'en'} 36 | if len(tests_to_run) == 0 or i in tests_to_run: 37 | r = requests.get('http://localhost:8081/test', headers=headers) 38 | printResponse(r, i) 39 | 40 | # --- 41 | i += 1 42 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get with Accept-Language: fr && Accept-Charset: utf8 \n" + bcolors.ENDC) 43 | headers = {'Accept-Language': 'fr', 'Accept-Charset': 'utf8'} 44 | if len(tests_to_run) == 0 or i in tests_to_run: 45 | r = requests.get('http://localhost:8081/test', headers=headers) 46 | printResponse(r, i) 47 | 48 | # --- 49 | i += 1 50 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get with Accept-Charset: iso-8859-1\n" + bcolors.ENDC) 51 | headers = {'Accept-Charset': 'iso-8859-1'} 52 | if len(tests_to_run) == 0 or i in tests_to_run: 53 | r = requests.get('http://localhost:8081/test', headers=headers) 54 | printResponse(r, i) 55 | 56 | # --- 57 | i += 1 58 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get with cgi\n" + bcolors.ENDC) 59 | if len(tests_to_run) == 0 or i in tests_to_run: 60 | r = requests.get('http://localhost:8081/index.cgi') 61 | printResponse(r, i) 62 | 63 | # --- 64 | i += 1 65 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get with auto_index\n" + bcolors.ENDC) 66 | if len(tests_to_run) == 0 or i in tests_to_run: 67 | r = requests.get('http://localhost:8081/auto_index') 68 | printResponse(r, i) 69 | 70 | # --- 71 | i += 1 72 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Get without auto_index\n" + bcolors.ENDC) 73 | if len(tests_to_run) == 0 or i in tests_to_run: 74 | r = requests.get('http://localhost:8081/no_auto_index') 75 | printResponse(r, i) 76 | -------------------------------------------------------------------------------- /project/v1/tests/headTests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | import os 4 | import sys 5 | 6 | tests_to_run = [] 7 | ac = len(sys.argv) 8 | if ac > 1: 9 | for av in sys.argv[1:]: 10 | # print ("to run: test", av) 11 | tests_to_run.append(int(av)) 12 | 13 | from printReqAndResp import * 14 | 15 | i = 0 16 | # --- 17 | 18 | i += 1 19 | print (bcolors.OKBLUE + "\n" + str(i) + ". Basic test head\n" + bcolors.ENDC) 20 | if len(tests_to_run) == 0 or i in tests_to_run: 21 | r = requests.head('http://localhost:8081/') 22 | printResponse(r, i) 23 | 24 | # --- 25 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test head fr\n" + bcolors.ENDC) 26 | i += 1 27 | headers = {'Accept-Language': 'fr'} 28 | if len(tests_to_run) == 0 or i in tests_to_run: 29 | r = requests.head('http://localhost:8081/test', headers=headers) 30 | printResponse(r, i) 31 | 32 | # --- 33 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test head en\n" + bcolors.ENDC) 34 | i += 1 35 | headers = {'Accept-Language': 'en'} 36 | if len(tests_to_run) == 0 or i in tests_to_run: 37 | r = requests.head('http://localhost:8081/test', headers=headers) 38 | printResponse(r, i) 39 | 40 | # --- 41 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test yolo (404)\n" + bcolors.ENDC) 42 | i += 1 43 | headers = {'Accept-Language': 'fr', 'Accept-Charset': 'utf8'} 44 | if len(tests_to_run) == 0 or i in tests_to_run: 45 | r = requests.head('http://localhost:8081/test/yolo', headers=headers) 46 | printResponse(r, i) 47 | 48 | # --- 49 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test head iso-8859-1\n" + bcolors.ENDC) 50 | i += 1 51 | headers = {'Accept-Charset': 'iso-8859-1'} 52 | if len(tests_to_run) == 0 or i in tests_to_run: 53 | r = requests.head('http://localhost:8081/test', headers=headers) 54 | printResponse(r, i) 55 | -------------------------------------------------------------------------------- /project/v1/tests/optionsTests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | import os 4 | import sys 5 | 6 | tests_to_run = [] 7 | ac = len(sys.argv) 8 | if ac > 1: 9 | for av in sys.argv[1:]: 10 | # print ("to run: test", av) 11 | tests_to_run.append(int(av)) 12 | 13 | from printReqAndResp import * 14 | 15 | i = 0 16 | 17 | # --- 18 | print (bcolors.OKBLUE + "\n" + ". Options renvoie les METHODS autorisées par la location\n" + bcolors.ENDC) 19 | 20 | # --- 21 | i += 1 22 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Options dans / \n" + bcolors.ENDC) 23 | if len(tests_to_run) == 0 or i in tests_to_run: 24 | r = requests.options('http://localhost:8081/') 25 | printResponse(r, i) 26 | 27 | # --- 28 | i += 1 29 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Options dans /delete \n" + bcolors.ENDC) 30 | headers = {'Accept-Language': 'fr'} 31 | if len(tests_to_run) == 0 or i in tests_to_run: 32 | r = requests.options('http://localhost:8081/delete', headers=headers) 33 | printResponse(r, i) 34 | 35 | # --- 36 | i += 1 37 | print (bcolors.OKBLUE + "\n" + str(i) + ". Test Options dans /auto_index \n" + bcolors.ENDC) 38 | headers = {'Accept-Language': 'en'} 39 | if len(tests_to_run) == 0 or i in tests_to_run: 40 | r = requests.options('http://localhost:8081/auto_index', headers=headers) 41 | printResponse(r, i) 42 | -------------------------------------------------------------------------------- /project/v1/tests/postTests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | import os 4 | import sys 5 | 6 | tests_to_run = [] 7 | ac = len(sys.argv) 8 | if ac > 1: 9 | for av in sys.argv[1:]: 10 | # print ("to run: test", av) 11 | tests_to_run.append(int(av)) 12 | 13 | from printReqAndResp import * 14 | 15 | i = 0 16 | 17 | # --- 18 | i += 1 19 | print (bcolors.OKBLUE + "\n" + str(i) + ". http://localhost:8082/text \n" + bcolors.ENDC) 20 | payload = "hello world ! ici le monde" 21 | headers_adds = { 22 | "Content-Type": "text/plain", 23 | } 24 | if len(tests_to_run) == 0 or i in tests_to_run: 25 | r = requests.post('http://localhost:8082/text', payload, headers=headers_adds) 26 | printResponse(r, i) 27 | 28 | # --- 29 | i += 1 30 | print (bcolors.OKBLUE + "\n" + str(i) + ". http://localhost:8082/TEST4\n" + bcolors.ENDC) 31 | payload = "txt=this_is_a_query_string_test" 32 | headers_adds = { 33 | "Content-Type": "application/x-www-form-urlencoded", 34 | } 35 | if len(tests_to_run) == 0 or i in tests_to_run: 36 | r = requests.post('http://localhost:8082/TEST4', payload, headers=headers_adds) 37 | printResponse(r, i) 38 | 39 | # --- 40 | i += 1 41 | print (bcolors.OKBLUE + "\n" + str(i) + ". http://localhost:8082/test.cgi\n" + bcolors.ENDC) 42 | payload = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" 43 | headers_adds = { 44 | "Content-Type": "text/plain", 45 | } 46 | if len(tests_to_run) == 0 or i in tests_to_run: 47 | r = requests.post('http://localhost:8082/test.cgi', payload, headers=headers_adds) 48 | printResponse(r, i) 49 | 50 | # --- 51 | i += 1 52 | print (bcolors.OKBLUE + "\n" + str(i) + ". http://localhost:8082/bj sans cgi \n" + bcolors.ENDC) 53 | body = "bonjour" 54 | if len(tests_to_run) == 0 or i in tests_to_run: 55 | r = requests.post('http://localhost:8082/bj', data=body) 56 | printResponse(r, i) 57 | 58 | # --- 59 | from requests.auth import HTTPBasicAuth 60 | i += 1 61 | print (bcolors.OKBLUE + "\n" + str(i) + ". POST http://localhost:8082/ avec auth \n" + bcolors.ENDC) 62 | if len(tests_to_run) == 0 or i in tests_to_run: 63 | r = requests.post('http://localhost:8082/', auth=HTTPBasicAuth('user', 'pass')) 64 | printResponse(r, i) 65 | 66 | # --- 67 | i += 1 68 | print (bcolors.OKBLUE + "\n" + str(i) + ". http://localhost:8082/test.cgi avec body de 1K:\n" + bcolors.ENDC) 69 | body = "x" * 1000 70 | if len(tests_to_run) == 0 or i in tests_to_run: 71 | r = requests.post('http://localhost:8082/test.cgi', data=body) 72 | printResponse(r, i) 73 | 74 | # --- 75 | i += 1 76 | print (bcolors.OKBLUE + "\n" + str(i) + ". POST http://localhost:8082/post_body with a size of 200:\n" + bcolors.ENDC) 77 | body = "x" * 200 78 | headers = { 79 | "Content-Type": "test/file", 80 | "Accept-Encoding": "gzip", 81 | "Connection" : "keep-alive" } 82 | if len(tests_to_run) == 0 or i in tests_to_run: 83 | r = requests.post('http://localhost:8082/post_body', data=body, headers=headers) 84 | printResponse(r, i) 85 | -------------------------------------------------------------------------------- /project/v1/tests/printReqAndResp.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | 4 | class bcolors: 5 | HEADER = '\033[95m' 6 | OKBLUE = '\033[94m' 7 | OKGREEN = '\033[92m' 8 | WARNING = '\033[93m' 9 | FAIL = '\033[91m' 10 | ENDC = '\033[0m' 11 | BOLD = '\033[1m' 12 | UNDERLINE = '\033[4m' 13 | 14 | def props(cls): 15 | return [i for i in cls.__dict__.keys() if i[:1] != '_'] 16 | 17 | def printResponse(r, i): 18 | 19 | print ("-------- REQUEST") 20 | # print (props(r.request)) 21 | print (r.request.method, "on", r.request.url) 22 | print () 23 | for h in r.request.headers: 24 | print (h + ": " + r.request.headers[h]) 25 | print () 26 | print ("Body:", r.request.body) 27 | 28 | print () 29 | 30 | print ("-------- RESPONSE") 31 | # print (props(r.raw)) 32 | print ("Status code:", r.status_code) 33 | print ("Reason:", r.raw.reason) 34 | print () 35 | for h in r.headers: 36 | print (h + ": " + r.headers[h]) 37 | print () 38 | print ("Body:", r.text) 39 | 40 | print ("------------------\n") 41 | -------------------------------------------------------------------------------- /project/v1/tests/putTests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests.auth import HTTPBasicAuth 3 | import os 4 | import sys 5 | 6 | tests_to_run = [] 7 | ac = len(sys.argv) 8 | if ac > 1: 9 | for av in sys.argv[1:]: 10 | # print ("to run: test", av) 11 | tests_to_run.append(int(av)) 12 | from printReqAndResp import * 13 | 14 | i = 0 15 | 16 | # --- 17 | i += 1 18 | print (bcolors.OKBLUE + "\n" + str(i) + ". Create file 'abc' == Update file 'abc' that doesn't exist:\n" + bcolors.ENDC) 19 | body = "abc" 20 | if len(tests_to_run) == 0 or i in tests_to_run: 21 | r = requests.put('http://localhost:8081/put_test/abc', data=body) 22 | printResponse(r, i) 23 | 24 | # --- 25 | i += 1 26 | print (bcolors.OKBLUE + "\n" + str(i) + ". Create file 'abc' that already exists == Update file 'abc':\n" + bcolors.ENDC) 27 | body = "def" 28 | if len(tests_to_run) == 0 or i in tests_to_run: 29 | r = requests.put('http://localhost:8081/put_test/abc', data=body) 30 | printResponse(r, i) 31 | 32 | # --- 33 | i += 1 34 | print (bcolors.OKBLUE + "\n" + str(i) + ". PUT on uri that doesn't exist:\n" + bcolors.ENDC) 35 | body = "this uri doesn't exist (should return 404)" 36 | if len(tests_to_run) == 0 or i in tests_to_run: 37 | r = requests.put('http://localhost:8081/doesnotexist/abc', data=body) 38 | printResponse(r, i) 39 | 40 | # --- 41 | i += 1 42 | print (bcolors.OKBLUE + "\n" + str(i) + ". PUT not allowed:\n" + bcolors.ENDC) 43 | body = "not allowed" 44 | if len(tests_to_run) == 0 or i in tests_to_run: 45 | r = requests.put('http://localhost:8081/delete', data=body) 46 | printResponse(r, i) 47 | 48 | # --- 49 | i += 1 50 | from requests.auth import HTTPBasicAuth 51 | print (bcolors.OKBLUE + "\n" + str(i) + ". Auth success:\n" + bcolors.ENDC) 52 | body = "auth success" 53 | if len(tests_to_run) == 0 or i in tests_to_run: 54 | r = requests.put('http://localhost:8081/auth/file', auth=HTTPBasicAuth('user', 'pass'), data=body) 55 | printResponse(r, i) 56 | 57 | # --- 58 | i += 1 59 | from requests.auth import HTTPBasicAuth 60 | print (bcolors.OKBLUE + "\n" + str(i) + ". Auth failed:\n" + bcolors.ENDC) 61 | body = "auth failed" 62 | if len(tests_to_run) == 0 or i in tests_to_run: 63 | r = requests.put('http://localhost:8081/auth/file', auth=HTTPBasicAuth('user', 'fail'), data=body) 64 | printResponse(r, i) 65 | 66 | # --- 67 | i += 1 68 | from requests.auth import HTTPBasicAuth 69 | print (bcolors.OKBLUE + "\n" + str(i) + ". max body -> 413:\n" + bcolors.ENDC) 70 | body = "413" * 100 71 | if len(tests_to_run) == 0 or i in tests_to_run: 72 | r = requests.put('http://localhost:8081/maxbody/index.html', data=body) 73 | printResponse(r, i) 74 | 75 | # --- 76 | i += 1 77 | from requests.auth import HTTPBasicAuth 78 | print (bcolors.OKBLUE + "\n" + str(i) + ". chunked:\n" + bcolors.ENDC) 79 | body = "14\r\nabcdefghijklmnopqrst\r\nA\r\n0123456789\r\n0\r\n\r\n" 80 | headers = {'Transfer-Encoding': 'chunked'} 81 | if len(tests_to_run) == 0 or i in tests_to_run: 82 | r = requests.put('http://localhost:8081/put_test/abc', data=body, headers=headers) 83 | printResponse(r, i) 84 | -------------------------------------------------------------------------------- /project/v1/www/CGI/cgi.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # get_post.cgi 3 | echo 'status code 200' 4 | 5 | echo 'Content-type: text/plain' echo 6 | echo "QS=$QUERY_STRING" 7 | read DATA 8 | echo "Data=$DATA" 9 | -------------------------------------------------------------------------------- /project/v1/www/CGI/cgi_tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/CGI/cgi_tester -------------------------------------------------------------------------------- /project/v1/www/CGI/perl_test: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl # env.cgi 2 | print "Content-type: text/html\n\n"; foreach $v (sort(keys(%ENV))) { print "$v --> $ENV{$v}
"; 3 | } 4 | -------------------------------------------------------------------------------- /project/v1/www/CGI/test_cgi_env.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "date.cgi" echo "" 3 | echo "

Date sur le serveur

" 4 | echo -n "On est le `date +%D`, il est " 5 | echo "`date +%H`h `date +%M`m" 6 | echo "" 7 | -------------------------------------------------------------------------------- /project/v1/www/CGI/ubuntu_cgi_tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/CGI/ubuntu_cgi_tester -------------------------------------------------------------------------------- /project/v1/www/methods/post/TEST4: -------------------------------------------------------------------------------- 1 | txt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_testtxt=this_is_a_query_string_test -------------------------------------------------------------------------------- /project/v1/www/methods/post/bj: -------------------------------------------------------------------------------- 1 | bonjourbonjourbonjourbonjourbonjourbonjourbonjourbonjourbonjour -------------------------------------------------------------------------------- /project/v1/www/methods/post/index.html: -------------------------------------------------------------------------------- 1 |

It works!

2 | -------------------------------------------------------------------------------- /project/v1/www/methods/post/post_body: -------------------------------------------------------------------------------- 1 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -------------------------------------------------------------------------------- /project/v1/www/methods/post/test.cgi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/methods/post/test.cgi -------------------------------------------------------------------------------- /project/v1/www/methods/post/text: -------------------------------------------------------------------------------- 1 | hello world ! ici le mondehello world ! ici le mondehello world ! ici le mondehello world ! ici le mondehello world ! ici le mondehello world ! ici le mondehello world ! ici le mondehello world ! ici le mondehello world ! ici le monde -------------------------------------------------------------------------------- /project/v1/www/methods/put/abc: -------------------------------------------------------------------------------- 1 | abcdefghijklmnopqrst0123456789 -------------------------------------------------------------------------------- /project/v1/www/methods/put/file: -------------------------------------------------------------------------------- 1 | auth success -------------------------------------------------------------------------------- /project/v1/www/perso/TEST4: -------------------------------------------------------------------------------- 1 | txt=this_is_a_query_string_testtxt=this_is_a_query_string_test -------------------------------------------------------------------------------- /project/v1/www/perso/bj: -------------------------------------------------------------------------------- 1 | bonjourbonjour -------------------------------------------------------------------------------- /project/v1/www/perso/delete/to_delete: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/perso/delete/to_delete -------------------------------------------------------------------------------- /project/v1/www/perso/error/400.html: -------------------------------------------------------------------------------- 1 |

Error 400

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/error/401.html: -------------------------------------------------------------------------------- 1 |

Error 401

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/error/404.html: -------------------------------------------------------------------------------- 1 |

Error 404

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/error/405.html: -------------------------------------------------------------------------------- 1 |

Error 405

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/error/413.html: -------------------------------------------------------------------------------- 1 |

Error 413

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/error/503.html: -------------------------------------------------------------------------------- 1 |

Error 503

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/index.cgi: -------------------------------------------------------------------------------- 1 |

It works!

It works!

-------------------------------------------------------------------------------- /project/v1/www/perso/post_body: -------------------------------------------------------------------------------- 1 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -------------------------------------------------------------------------------- /project/v1/www/perso/test.cgi: -------------------------------------------------------------------------------- 1 | 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -------------------------------------------------------------------------------- /project/v1/www/perso/test/bonjour: -------------------------------------------------------------------------------- 1 | bonjourbonjourbonjourbonjourbonjour -------------------------------------------------------------------------------- /project/v1/www/perso/test/index.html: -------------------------------------------------------------------------------- 1 |

It works!

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/test/index.html.en: -------------------------------------------------------------------------------- 1 |

en en en !

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/test/index.html.fr: -------------------------------------------------------------------------------- 1 |

fr fr fr !

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/test/index.html.fr.utf8: -------------------------------------------------------------------------------- 1 |

fr fr fr / utf-8 !

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/test/index.html.iso-8859-1: -------------------------------------------------------------------------------- 1 |

iso-8859-1 !

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/test/index.html.utf8: -------------------------------------------------------------------------------- 1 |

utf-8 !

2 | -------------------------------------------------------------------------------- /project/v1/www/perso/text: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/perso/text -------------------------------------------------------------------------------- /project/v1/www/perso/vide/listing1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/perso/vide/listing1 -------------------------------------------------------------------------------- /project/v1/www/perso/vide/listing2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/perso/vide/listing2 -------------------------------------------------------------------------------- /project/v1/www/perso/vide/listing3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/perso/vide/listing3 -------------------------------------------------------------------------------- /project/v1/www/tester/CGI/cgi_tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/tester/CGI/cgi_tester -------------------------------------------------------------------------------- /project/v1/www/tester/CGI/perl_test: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl # env.cgi 2 | print "Content-type: text/html\n\n"; foreach $v (sort(keys(%ENV))) { print "$v --> $ENV{$v}
"; 3 | } 4 | -------------------------------------------------------------------------------- /project/v1/www/tester/CGI/test_cgi_env.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # date.cgi 3 | echo "Content-type: text/html" 4 | echo 5 | #Creation du corps du document 6 | echo "date.cgi" echo "" 7 | echo "

Date sur le serveur

" 8 | echo -n "On est le `date +%D`, il est " 9 | echo "`date +%H`h `date +%M`m" 10 | echo "" 11 | -------------------------------------------------------------------------------- /project/v1/www/tester/YoupiBanane/Yeah/not_happy.bad_extension: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/project/v1/www/tester/YoupiBanane/Yeah/not_happy.bad_extension -------------------------------------------------------------------------------- /project/v1/www/tester/YoupiBanane/nop/other.pouic: -------------------------------------------------------------------------------- 1 | other.pouic -------------------------------------------------------------------------------- /project/v1/www/tester/YoupiBanane/nop/youpi.bad_extension: -------------------------------------------------------------------------------- 1 | /nop/youpi.bad_extension -------------------------------------------------------------------------------- /project/v1/www/tester/YoupiBanane/youpi.bad_extension: -------------------------------------------------------------------------------- 1 | ./youpi.bad_extension -------------------------------------------------------------------------------- /project/v1/www/tester/YoupiBanane/youpi.bla: -------------------------------------------------------------------------------- 1 | youpi.bla -------------------------------------------------------------------------------- /project/v1/www/tester/abc: -------------------------------------------------------------------------------- 1 | abcdefghijklmnopqrst0123456789abcdefabcdefghijklmnopqrst0123456789 -------------------------------------------------------------------------------- /project/v1/www/tester/error/400.html: -------------------------------------------------------------------------------- 1 |

Error 400

2 | -------------------------------------------------------------------------------- /project/v1/www/tester/error/401.html: -------------------------------------------------------------------------------- 1 |

Error 401

2 | -------------------------------------------------------------------------------- /project/v1/www/tester/error/404.html: -------------------------------------------------------------------------------- 1 |

Error 404

2 | -------------------------------------------------------------------------------- /project/v1/www/tester/error/405.html: -------------------------------------------------------------------------------- 1 |

Error 405

2 | -------------------------------------------------------------------------------- /project/v1/www/tester/error/413.html: -------------------------------------------------------------------------------- 1 |

Error 413

2 | -------------------------------------------------------------------------------- /project/v1/www/tester/error/503.html: -------------------------------------------------------------------------------- 1 |

Error 503

2 | -------------------------------------------------------------------------------- /project/v1/www/tester/file: -------------------------------------------------------------------------------- 1 | auth successauth success -------------------------------------------------------------------------------- /project/v1/www/tester/getindex.html: -------------------------------------------------------------------------------- 1 |

getindex.html

-------------------------------------------------------------------------------- /project/v1/www/tester/index.html: -------------------------------------------------------------------------------- 1 |

It works !

tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt -------------------------------------------------------------------------------- /subject/webserv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fberger-xyz/webserv/4cebde9e80644c8812631c82e38e7d194cfb943d/subject/webserv.pdf -------------------------------------------------------------------------------- /utils/commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "make fclean" 4 | make fclean 2> /dev/null || true 5 | 6 | echo 7 | echo "git add ." 8 | git add . 9 | 10 | echo 11 | echo "Enter a commit message:" 12 | MSG= read -r -p "> " msg 13 | echo "git commit -m $msg" 14 | git commit -m $msg 15 | 16 | echo 17 | echo "Push ? [Yy/Nn]:" 18 | read -p "> " -n 1 -r 19 | echo 20 | if [[ $REPLY =~ ^[Yy]$ ]] 21 | then 22 | echo "git push origin master" 23 | git push origin master 24 | fi 25 | 26 | -------------------------------------------------------------------------------- /utils/memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path1="/proc/" 4 | # echo $path1 5 | 6 | path2=$(ps -a | grep webserv | awk '{print $1, $8}') 7 | path2=$(echo $path2 | xargs) 8 | # echo $path2 9 | 10 | path3="/status" 11 | # echo $path3 12 | 13 | path="${path1}${path2}${path3}" 14 | echo $path 15 | 16 | # while 1 ; do grep VmPeak /proc/$wpid/status ; sleep 3 ; done 17 | while [ 1 ] ; do 18 | grep VmPeak $path 19 | sleep 0.25 20 | done 21 | --------------------------------------------------------------------------------