3 * Copyright (C) 2001-2008, Eduardo Silva P.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include <sys/socket.h>
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
37 #include <sys/types.h>
41 #include "http_status.h"
43 struct request
*parse_client_request(struct client_request
*cr
)
45 int i
, init_block
=0, n_blocks
=0, offset
=0;
46 int length_buf
=0, length_end
=0;
48 char *string_end
=0, *check_normal_string
=0, *check_old_string
=0;
50 struct request
*cr_buf
=0, *cr_search
=0;
52 check_normal_string
= strstr(cr
->body
, NORMAL_STRING_END
);
53 check_old_string
= strstr(cr
->body
, OLD_STRING_END
);
54 if(check_normal_string
)
62 string_end
= NORMAL_STRING_END
;
63 length_end
= LEN_NORMAL_STRING_END
;
67 else if(check_old_string
)
69 string_end
= OLD_STRING_END
;
70 length_end
= LEN_OLD_STRING_END
;
74 length_buf
= cr
->body_length
;
77 for(i
=0; i
<= length_buf
-length_end
; i
++)
79 if(strncmp(cr
->body
+i
, string_end
, length_end
)==0)
81 /* Allocating request block */
82 block
= m_copy_string(cr
->body
, init_block
, i
);
84 cr_buf
= alloc_request();
86 cr_buf
->method
= mk_http_method_get(cr_buf
->body
);
89 i
= init_block
= (i
+offset
) + length_end
;
91 /* Looking for POST data */
92 if(cr_buf
->method
== HTTP_METHOD_POST
)
94 cr_buf
->post_variables
= M_Get_POST_Vars(cr
->body
, i
, string_end
);
95 if(cr_buf
->post_variables
)
97 i
+= strlen(cr_buf
->post_variables
) + length_end
;
103 cr
->request
= cr_buf
;
106 cr_search
= cr
->request
;
109 if(cr_search
->next
==NULL
)
111 cr_search
->next
= cr_buf
;
116 cr_search
= cr_search
->next
;
124 /* Checking pipelining connection */
125 cr_search
= cr
->request
;
131 if(cr_search
->method
!=HTTP_METHOD_GET
&&
132 cr_search
->method
!=HTTP_METHOD_HEAD
)
137 cr_search
= cr_search
->next
;
140 if(pipelined
== FALSE
){
141 /* All pipelined requests must been GET method */
145 cr
->pipelined
= TRUE
;
150 // printf("*****************************************");
152 cr_search = cr->request;
154 printf("\n---BLOCK---:\n%s---END BLOCK---\n\n", cr_search->body);
156 cr_search = cr_search->next;
162 int mk_handler_read(int socket
)
165 struct client_request
*cr
;
167 cr
= mk_get_client_request_from_fd(socket
);
170 cr
= mk_create_client_request(socket
);
173 bytes
= read(socket
, cr
->body
+cr
->body_length
,
174 MAX_REQUEST_BODY
-cr
->body_length
-1);
177 if (errno
== EAGAIN
) {
191 cr
->body_length
+=bytes
;
192 efd
= mk_sched_get_thread_poll();
194 if(strncmp(cr
->body
+(cr
->body_length
-LEN_NORMAL_STRING_END
),
196 LEN_NORMAL_STRING_END
) == 0)
198 mk_epoll_socket_change_mode(efd
, socket
,
201 else if(strncmp(cr
->body
+(cr
->body_length
-LEN_OLD_STRING_END
),
203 LEN_OLD_STRING_END
) == 0)
205 mk_epoll_socket_change_mode(efd
, socket
,
213 int mk_handler_write(int socket
, struct client_request
*cr
)
215 int bytes
, final_status
=0;
216 struct request
*p_request
;
219 * Get node from schedule list node which contains
220 * the information regarding to the current thread
222 cr
= mk_get_client_request_from_fd(socket
);
231 if(!parse_client_request(cr
))
237 p_request
= cr
->request
;
241 /* Request not processed */
242 if(p_request
->bytes_to_send
< 0)
244 //printf("\nREQUEST::going process");
247 final_status
= Process_Request(cr
, p_request
);
249 /* Request with data to send */
250 else if(p_request
->bytes_to_send
>0)
252 //printf("\nREQUEST::trying to send data :/");
254 bytes
= SendFile(socket
, p_request
);
255 final_status
= bytes
;
257 else if(p_request
->bytes_to_send
== 0)
259 //printf("\n*** to send = 0");
263 * If we got an error, we don't want to parse
264 * and send information for another pipelined request
266 if(final_status
<0 || final_status
> 0)
270 //write_log(p_request->log);
271 p_request
= p_request
->next
;
274 /* If we are here, is because all pipelined request were
275 * processed successfully, let's return 0;
280 int Process_Request(struct client_request
*cr
, struct request
*s_request
)
285 status
= Process_Request_Header(s_request
);
291 s_request
->user_home
=VAR_OFF
;
293 /* Valid request URI? */
294 if(s_request
->uri_processed
==NULL
){
295 Request_Error(M_CLIENT_BAD_REQUEST
, cr
, s_request
, 1, s_request
->log
);
299 /* URL it's Allowed ? */
300 if(Deny_Check(s_request
, cr
->client_ip
)==-1) {
301 s_request
->log
->final_response
=M_CLIENT_FORBIDDEN
;
302 Request_Error(M_CLIENT_FORBIDDEN
, cr
, s_request
,1,s_request
->log
);
307 /* HTTP/1.1 needs Host header */
308 if(!s_request
->host
&& s_request
->protocol
==HTTP_PROTOCOL_11
){
309 s_request
->log
->final_response
=M_CLIENT_BAD_REQUEST
;
310 Request_Error(M_CLIENT_BAD_REQUEST
, cr
, s_request
,1,s_request
->log
);
314 /* Method not allowed ? */
315 if(s_request
->method
==METHOD_NOT_ALLOWED
){
316 s_request
->log
->final_response
=M_CLIENT_METHOD_NOT_ALLOWED
;
317 Request_Error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
, s_request
,1,s_request
->log
);
321 /* Validating protocol version */
322 if(s_request
->protocol
== HTTP_PROTOCOL_UNKNOWN
)
324 s_request
->log
->final_response
=M_SERVER_HTTP_VERSION_UNSUP
;
325 Request_Error(M_SERVER_HTTP_VERSION_UNSUP
, cr
, s_request
,1,s_request
->log
);
331 host
=VHOST_Find(s_request
->host
);
334 s_request
->host_conf
= host
;
337 s_request
->host_conf
= config
->hosts
;
341 s_request
->host_conf
= config
->hosts
;
343 s_request
->log
->host_conf
= s_request
->host_conf
;
346 if(s_request
->host_conf
->scriptalias
!=NULL
){
349 len
= strlen(s_request
->host_conf
->scriptalias
[0]);
350 if((strncmp(s_request
->host_conf
->scriptalias
[0], s_request
->uri_processed
, len
))==0){
352 cgi_status
=M_CGI_main(cr
, s_request
, s_request
->log
, s_request
->body
);
354 -1 : Fallo de permisos
356 -3 : Internal Server Error
358 if(cgi_status
==M_CGI_TIMEOUT
|| cgi_status
==M_CGI_INTERNAL_SERVER_ERR
){
359 Request_Error(s_request
->log
->final_response
,
360 cr
, s_request
, 1, s_request
->log
);
366 /* is requesting an user home directory ? */
367 if(strncmp(s_request
->uri_processed
, USER_HOME_STRING
,
368 strlen(USER_HOME_STRING
))==0 && config
->user_dir
){
369 if(User_main(cr
, s_request
)!=0)
375 * Handling method requested */
376 if(s_request
->method
==HTTP_METHOD_GET
|| s_request
->method
==HTTP_METHOD_HEAD
)
378 status
=mk_http_init(cr
, s_request
);
381 if(s_request
->method
==HTTP_METHOD_POST
){
382 if((status
=M_METHOD_Post(cr
, s_request
))==-1){
385 status
= mk_http_init(cr
, s_request
);
392 /* Return a struct with method, URI , protocol version
393 and all static headers defined here sent in request */
394 int Process_Request_Header(struct request
*sr
)
396 int uri_init
=0, uri_end
=0;
397 int query_init
=0, query_end
=0;
398 int prot_init
=0, prot_end
=0;
402 //sr->method = sr->log->method = mk_http_method_get(sr->body);
403 sr
->method_str
= (char *) mk_http_method_check_str(sr
->method
);
406 uri_init
= str_search(sr
->body
, " ",1) + 1;
407 uri_end
= str_search(sr
->body
+uri_init
, " ",1) + uri_init
;
409 if(uri_end
< uri_init
)
415 query_init
= str_search(sr
->body
+uri_init
, "?", 1);
416 if(query_init
> 0 && query_init
<= uri_end
)
418 query_init
+=uri_init
+1;
420 uri_end
= query_init
- 1;
421 sr
->query_string
= m_copy_string(sr
->body
, query_init
, query_end
);
424 /* Request URI Part 2 */
425 sr
->uri
= sr
->log
->uri
= (char *)m_copy_string(sr
->body
, uri_init
, uri_end
);
427 if(strlen(sr
->uri
)<1)
433 prot_init
=str_search(sr
->body
+uri_init
+1," ",1)+uri_init
+2;
435 if(str_search(sr
->body
, "\r\n",2)>0){
436 prot_end
= str_search(sr
->body
, "\r\n",2);
439 prot_end
= str_search(sr
->body
, "\n",1);
442 if(prot_end
!=prot_init
&& prot_end
>0){
443 str_prot
= m_copy_string(sr
->body
, prot_init
, prot_end
);
444 sr
->protocol
= sr
->log
->protocol
= mk_http_protocol_check(str_prot
);
449 sr
->uri_processed
= get_real_string( sr
->uri
);
450 if(!sr
->uri_processed
)
452 sr
->uri_processed
= sr
->uri
;
453 sr
->uri_twin
= VAR_ON
;
457 if(mk_strcasestr(sr
->body
, RH_HOST
))
459 char *tmp
= Request_Find_Variable(sr
->body
, RH_HOST
);
461 /* is host formated something like xxxxx:yy ???? */
462 if(tmp
!=NULL
&& strstr(tmp
, ":") != NULL
){
466 pos_sep
= str_search(tmp
, ":",1);
467 sr
->host
= m_copy_string(tmp
, 0, pos_sep
);
468 port
= m_copy_string(tmp
, pos_sep
, strlen(tmp
));
474 sr
->host
=tmp
; /* maybe null */
475 sr
->port
=config
->standard_port
;
482 /* Looking for headers */
483 sr
->accept
= Request_Find_Variable(sr
->body
, RH_ACCEPT
);
484 sr
->accept_charset
= Request_Find_Variable(sr
->body
, RH_ACCEPT_CHARSET
);
485 sr
->accept_encoding
= Request_Find_Variable(sr
->body
, RH_ACCEPT_ENCODING
);
486 sr
->accept_language
= Request_Find_Variable(sr
->body
, RH_ACCEPT_LANGUAGE
);
487 sr
->cookies
= Request_Find_Variable(sr
->body
, RH_COOKIE
);
488 sr
->connection
= Request_Find_Variable(sr
->body
, RH_CONNECTION
);
489 sr
->referer
= Request_Find_Variable(sr
->body
, RH_REFERER
);
490 sr
->user_agent
= Request_Find_Variable(sr
->body
, RH_USER_AGENT
);
491 sr
->range
= Request_Find_Variable(sr
->body
, RH_RANGE
);
492 sr
->if_modified_since
= Request_Find_Variable(sr
->body
, RH_IF_MODIFIED_SINCE
);
494 /* Checking keepalive */
495 sr
->keep_alive
=VAR_OFF
;
498 if(sr
->protocol
==HTTP_PROTOCOL_11
||
499 sr
->protocol
==HTTP_PROTOCOL_10
)
501 if(mk_strcasestr(sr
->connection
,"Keep-Alive"))
503 sr
->keep_alive
=VAR_ON
;
510 /* Return value of some variable sent in request */
511 char *Request_Find_Variable(char *request_body
, char *string
)
513 int pos_init_var
=0, pos_end_var
=0;
517 /* looking for string on request_body ??? */
518 if(!(t
=(char *)mk_strcasestr(request_body
, string
)))
523 pos_init_var
= strlen(string
);
524 if((t
+pos_init_var
)[0]==' ')
529 pos_end_var
= str_search((char *)t
, "\n", 1) - 1;
532 pos_end_var
= strlen(t
);
534 if(pos_init_var
<=0 || pos_end_var
<=0){
537 var_value
= m_copy_string(t
, pos_init_var
, pos_end_var
);
539 return (char *) var_value
;
542 /* Look for some index.xxx in pathfile */
543 char *FindIndex(char *pathfile
)
546 struct indexfile
*aux_index
;
548 aux_index
=first_index
;
550 while(aux_index
!=NULL
) {
551 if(pathfile
[strlen(pathfile
)-1]=='/')
552 file_aux
=m_build_buffer("%s/%s",pathfile
,aux_index
->indexname
);
554 file_aux
=m_build_buffer("%s%s",pathfile
,aux_index
->indexname
);
556 if(access(file_aux
,F_OK
)==0) {
558 return (char *) aux_index
->indexname
;
561 aux_index
=aux_index
->next
;
567 /* Send error responses */
568 void Request_Error(int num_error
, struct client_request
*cr
,
569 struct request
*s_request
, int debug
, struct log_info
*s_log
)
571 char *page_default
=0, *aux_message
=0;
574 s_log
=M_malloc(sizeof(struct log_info
));
578 case M_CLIENT_BAD_REQUEST
:
579 page_default
=Set_Page_Default("Bad Request", "", s_request
->host_conf
->host_signature
);
580 s_log
->error_msg
=m_build_buffer("[error 400] Bad request");
583 case M_CLIENT_FORBIDDEN
:
584 page_default
=Set_Page_Default("Forbidden", s_request
->uri
, s_request
->host_conf
->host_signature
);
585 s_log
->error_msg
=m_build_buffer("[error 403] Forbidden %s",s_request
->uri
);
588 case M_CLIENT_NOT_FOUND
:
589 aux_message
= m_build_buffer("The requested URL %.100s was not found on this server.", (char *) s_request
->uri
);
590 page_default
=Set_Page_Default("Not Found", aux_message
, s_request
->host_conf
->host_signature
);
591 s_log
->error_msg
=m_build_buffer("[error 404] Not Found %s",s_request
->uri
);
594 case M_CLIENT_METHOD_NOT_ALLOWED
:
595 page_default
=Set_Page_Default("Method Not Allowed",
597 s_request
->host_conf
->host_signature
);
599 s_log
->final_response
=M_CLIENT_METHOD_NOT_ALLOWED
;
600 s_log
->error_msg
=m_build_buffer("[error 405] Method Not Allowed");
603 case M_CLIENT_REQUEST_TIMEOUT
:
604 s_log
->status
=S_LOG_OFF
;
605 s_log
->error_msg
=m_build_buffer("[error 408] Request Timeout");
608 case M_CLIENT_LENGHT_REQUIRED
:
609 s_log
->error_msg
=m_build_buffer("[error 411] Length Required");
612 case M_SERVER_INTERNAL_ERROR
:
613 aux_message
= m_build_buffer("Problems found running %s ",s_request
->uri
);
614 page_default
=Set_Page_Default("Internal Server Error",aux_message
, s_request
->host_conf
->host_signature
);
615 s_log
->error_msg
=m_build_buffer("[error 411] Internal Server Error %s",s_request
->uri
);
618 case M_SERVER_HTTP_VERSION_UNSUP
:
619 page_default
=Set_Page_Default("HTTP Version Not Supported"," ", s_request
->host_conf
->host_signature
);
620 s_log
->error_msg
=m_build_buffer("[error 505] HTTP Version Not Supported");
624 s_log
->final_response
=num_error
;
626 s_request
->headers
->status
= num_error
;
627 s_request
->headers
->content_length
= 0;
628 s_request
->headers
->location
= NULL
;
629 s_request
->headers
->cgi
= SH_NOCGI
;
630 s_request
->headers
->pconnections_left
= 0;
631 s_request
->headers
->last_modified
= NULL
;
633 if(aux_message
) M_free(aux_message
);
636 s_request
->headers
->content_type
= NULL
;
638 s_request
->headers
->content_type
= m_build_buffer("text/html");
640 M_METHOD_send_headers(cr
->socket
, cr
, s_request
, s_log
);
643 fdprintf(cr
->socket
, NO_CHUNKED
, "%s", page_default
);
644 M_free(page_default
);
648 /* Build error page */
649 char *Set_Page_Default(char *title
, char *message
, char *signature
)
653 page
= m_build_buffer("<HTML><BODY><H1>%s</H1>%s<BR><HR> \
654 <ADDRESS>%s</ADDRESS></BODY></HTML>", title
, message
, signature
);
655 return (char *) page
;
658 /* Set Timeout for send() and recv() */
659 int Socket_Timeout(int s
, char *buf
, int len
, int timeout
, int recv_send
)
662 time_t init_time
, max_time
;
666 init_time
=time(NULL
);
667 max_time
= init_time
+ timeout
;
675 if(recv_send
==ST_RECV
)
676 n
=select(s
+1,&fds
,NULL
,NULL
,&tv
); // recv
678 n
=select(s
+1,NULL
,&fds
,NULL
,&tv
); // send
686 //pthread_kill(pthread_self(), SIGPIPE);
690 if(recv_send
==ST_RECV
){
691 status
=recv(s
,buf
,len
, 0);
694 status
=send(s
,buf
,len
, 0);
698 if(time(NULL
) >= max_time
){
699 //pthread_kill(pthread_self(), SIGPIPE);
706 /* Create a memory allocation in order to handle the request data */
707 struct request
*alloc_request()
709 struct request
*request
=0;
711 request
= (struct request
*) M_malloc(sizeof(struct request
));
712 request
->log
= (struct log_info
*) M_malloc(sizeof(struct log_info
));
713 //request->log->ip=PutIP(remote);
715 request
->status
=VAR_OFF
; /* Request not processed yet */
716 request
->make_log
=VAR_ON
; /* build log file of this request ? */
717 request
->query_string
=NULL
;
719 //request->log->datetime=PutTime();
720 request
->log
->final_response
=M_HTTP_OK
;
721 request
->log
->status
=S_LOG_ON
;
722 request
->status
=VAR_ON
;
723 request
->method
=METHOD_NOT_FOUND
;
726 request
->uri_processed
= NULL
;
727 request
->uri_twin
= VAR_OFF
;
729 request
->accept
= NULL
;
730 request
->accept_language
= NULL
;
731 request
->accept_encoding
= NULL
;
732 request
->accept_charset
= NULL
;
733 request
->content_type
= NULL
;
734 request
->connection
= NULL
;
735 request
->cookies
= NULL
;
736 request
->host
= NULL
;
737 request
->if_modified_since
= NULL
;
738 request
->last_modified_since
= NULL
;
739 request
->range
= NULL
;
740 request
->referer
= NULL
;
741 request
->resume
= NULL
;
742 request
->user_agent
= NULL
;
743 request
->post_variables
= NULL
;
745 request
->user_uri
= NULL
;
746 request
->query_string
= NULL
;
748 request
->virtual_user
= NULL
;
749 request
->script_filename
= NULL
;
750 request
->real_path
= NULL
;
751 request
->host_conf
= config
->hosts
;
753 request
->bytes_to_send
= -1;
754 request
->bytes_offset
= 0;
755 request
->fd_file
= -1;
757 request
->headers
= (struct header_values
*) M_malloc(sizeof(struct header_values
));
758 request
->headers
->content_type
= NULL
;
759 request
->headers
->last_modified
= NULL
;
760 request
->headers
->location
= NULL
;
761 request
->headers
->ranges
[0]=-1;
762 request
->headers
->ranges
[1]=-1;
764 return (struct request
*) request
;
767 void free_list_requests(struct client_request
*cr
)
769 struct request
*sr
=0, *before
=0;
775 sr
= before
= cr
->request
;
783 while(before
->next
!=sr
){
784 before
= before
->next
;
796 void free_request(struct request
*sr
)
798 /* I hate it, but I don't know another light way :( */
804 M_free(sr
->headers
->location
);
805 M_free(sr
->headers
->last_modified
);
807 M_free(sr->headers->content_type);
808 headers->content_type never it's allocated
809 with malloc or something, so we don't need
810 to free it, the value has been freed before
811 in M_METHOD_Get_and_Head(struct request *sr)
813 this BUG was reported by gentoo team.. thanks guys XD
821 M_free(sr
->log
->error_msg
);
828 if(sr
->uri_twin
==VAR_OFF
)
830 M_free(sr
->uri_processed
);
834 M_free(sr
->accept_language
);
835 M_free(sr
->accept_encoding
);
836 M_free(sr
->accept_charset
);
837 M_free(sr
->content_type
);
838 M_free(sr
->connection
);
841 M_free(sr
->if_modified_since
);
842 M_free(sr
->last_modified_since
);
846 M_free(sr
->user_agent
);
847 M_free(sr
->post_variables
);
849 M_free(sr
->user_uri
);
850 M_free(sr
->query_string
);
852 M_free(sr
->virtual_user
);
853 M_free(sr
->script_filename
);
854 M_free(sr
->real_path
);
858 /* Create a client request struct and put it on the
861 struct client_request
*mk_create_client_request(int socket
)
863 struct client_request
*request_handler
, *cr
, *aux
;
865 cr
= M_malloc(sizeof(struct client_request
));
866 cr
->pipelined
= FALSE
;
867 cr
->counter_connections
= 0;
870 cr
->client_ip
= mk_socket_get_ip(socket
);
872 cr
->body
= M_malloc(MAX_REQUEST_BODY
);
873 request_handler
= mk_sched_get_request_handler();
878 request_handler
= cr
;
881 aux
= request_handler
;
882 while(aux
->next
!=NULL
)
890 mk_sched_set_request_handler(request_handler
);
891 request_handler
= mk_sched_get_request_handler();
892 return (struct client_request
*) cr
;
895 struct client_request
*mk_get_client_request_from_fd(int socket
)
897 struct client_request
*request_handler
, *cr
;
899 request_handler
= mk_sched_get_request_handler();
900 cr
= request_handler
;
903 if(cr
->socket
== socket
)
910 return (struct client_request
*) cr
;
914 * From thread sched_list_node "list", remove the client_request
917 struct client_request
*mk_remove_client_request(int socket
)
919 struct client_request
*request_handler
, *cr
, *aux
;
921 request_handler
= mk_sched_get_request_handler();
922 cr
= request_handler
;
926 if(cr
->socket
== socket
)
928 if(cr
==request_handler
)
930 request_handler
= cr
->next
;
934 aux
= request_handler
;
939 aux
->next
= cr
->next
;
941 //free_list_requests(cr);
948 mk_sched_set_request_handler(request_handler
);