2 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
6 * Copyright (C) 2001-2008, Eduardo Silva P.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include <sys/socket.h>
38 #include <arpa/inet.h>
39 #include <netinet/in.h>
40 #include <sys/types.h>
45 #include "http_status.h"
50 #include "scheduler.h"
64 struct request
*mk_request_parse(struct client_request
*cr
)
66 int i
, n
, init_block
=0, n_blocks
=0;
68 struct request
*cr_buf
=0, *cr_search
=0;
72 for(i
=cr
->first_block_end
; i
<=cr
->body_length
-mk_endblock
.len
; i
++)
74 /* Allocating request block */
75 cr_buf
= mk_request_alloc();
78 cr_buf
->body
.data
= cr
->body
+init_block
;
79 cr_buf
->body
.len
= i
-init_block
;
81 if(i
==cr
->first_block_end
){
82 cr_buf
->method
= cr
->first_method
;
85 cr_buf
->method
= mk_http_method_get(cr_buf
->body
.data
);
89 cr_buf
->log
->ip
= cr
->ip
;
92 i
= init_block
= i
+ mk_endblock
.len
;
94 /* Looking for POST data */
95 if(cr_buf
->method
== HTTP_METHOD_POST
)
97 cr_buf
->post_variables
=
98 mk_method_post_get_vars(cr
->body
, i
);
100 if(cr_buf
->post_variables
.len
>= 0)
102 i
= init_block
= i
+cr_buf
->post_variables
.len
;
108 cr
->request
= cr_buf
;
111 cr_search
= cr
->request
;
114 if(cr_search
->next
==NULL
)
116 cr_search
->next
= cr_buf
;
121 cr_search
= cr_search
->next
;
126 n
= mk_string_search(cr
->body
+i
, mk_endblock
.data
);
136 /* Checking pipelining connection */
137 cr_search
= cr
->request
;
143 if(cr_search
->method
!=HTTP_METHOD_GET
&&
144 cr_search
->method
!=HTTP_METHOD_HEAD
)
149 cr_search
= cr_search
->next
;
152 if(pipelined
== FALSE
){
153 /* All pipelined requests must use GET method */
157 cr
->pipelined
= TRUE
;
162 printf("*****************************************");
164 cr_search = cr->request;
166 printf("\n---BLOCK---:\n%s---END BLOCK---\n\n", cr_search->body);
168 cr_search = cr_search->next;
175 int mk_handler_read(int socket
)
178 struct client_request
*cr
;
180 cr
= mk_request_client_get(socket
);
184 /* Note: Linux don't set TCP_NODELAY socket flag by default,
185 * also we set the client socket on non-blocking mode
187 mk_socket_set_tcp_nodelay(socket
);
188 mk_socket_set_nonblocking(socket
);
190 cr
= mk_request_client_create(socket
);
193 bytes
= read(socket
, cr
->body
+cr
->body_length
,
194 MAX_REQUEST_BODY
-cr
->body_length
);
197 if (errno
== EAGAIN
) {
201 mk_request_client_remove(socket
);
206 mk_request_client_remove(socket
);
212 cr
->body_length
+=bytes
;
213 cr
->body
[cr
->body_length
] = '\0';
215 if(mk_http_pending_request(cr
)==0){
216 efd
= mk_sched_get_thread_poll();
217 mk_epoll_socket_change_mode(efd
, socket
, MK_EPOLL_WRITE
);
219 else if(cr
->body_length
+1 >= MAX_REQUEST_BODY
)
221 /* Request is incomplete and our buffer is full,
224 mk_request_client_remove(socket
);
232 int mk_handler_write(int socket
, struct client_request
*cr
)
234 int bytes
, final_status
=0;
235 struct request
*p_request
;
238 * Get node from schedule list node which contains
239 * the information regarding to the current thread
241 cr
= mk_request_client_get(socket
);
250 if(!mk_request_parse(cr
))
256 p_request
= cr
->request
;
260 /* Request not processed */
261 if(p_request
->bytes_to_send
< 0)
263 final_status
= mk_request_process(cr
, p_request
);
265 /* Request with data to send */
266 else if(p_request
->bytes_to_send
>0)
268 bytes
= SendFile(socket
, p_request
);
269 final_status
= bytes
;
272 * If we got an error, we don't want to parse
273 * and send information for another pipelined request
279 else if(final_status
<= 0)
281 mk_logger_write_log(p_request
->log
, p_request
->host_conf
);
283 p_request
= p_request
->next
;
286 /* If we are here, is because all pipelined request were
287 * processed successfully, let's return 0;
292 int mk_request_process(struct client_request
*cr
, struct request
*s_request
)
297 status
= mk_request_header_process(s_request
);
304 switch(s_request
->method
)
306 case METHOD_NOT_ALLOWED
:
307 mk_request_error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
,
308 s_request
, 1, s_request
->log
);
310 case METHOD_NOT_FOUND
:
311 mk_request_error(M_SERVER_NOT_IMPLEMENTED
, cr
,
312 s_request
, 1, s_request
->log
);
316 s_request
->user_home
=VAR_OFF
;
317 s_request
->log
->method
= s_request
->method
;
319 /* Valid request URI? */
320 if(s_request
->uri_processed
==NULL
){
321 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
, 1,
326 /* URL it's Allowed ? */
327 if(Deny_Check(s_request
, cr
->ip
.data
)==-1) {
328 s_request
->log
->final_response
=M_CLIENT_FORBIDDEN
;
329 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, s_request
, 1,
335 /* HTTP/1.1 needs Host header */
336 if(!s_request
->host
.data
&& s_request
->protocol
==HTTP_PROTOCOL_11
){
337 s_request
->log
->final_response
=M_CLIENT_BAD_REQUEST
;
338 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
,1,
343 /* Method not allowed ? */
344 if(s_request
->method
==METHOD_NOT_ALLOWED
){
345 s_request
->log
->final_response
=M_CLIENT_METHOD_NOT_ALLOWED
;
346 mk_request_error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
, s_request
, 1,
351 /* Validating protocol version */
352 if(s_request
->protocol
== HTTP_PROTOCOL_UNKNOWN
)
355 s_request
->log
->final_response
=M_SERVER_HTTP_VERSION_UNSUP
;
356 mk_request_error(M_SERVER_HTTP_VERSION_UNSUP
, cr
, s_request
, 1,
361 if(s_request
->host
.data
)
363 host
=mk_config_host_find(s_request
->host
);
366 s_request
->host_conf
= host
;
369 s_request
->host_conf
= config
->hosts
;
373 s_request
->host_conf
= config
->hosts
;
375 s_request
->log
->host_conf
= s_request
->host_conf
;
377 /* is requesting an user home directory ? */
378 if(strncmp(s_request
->uri_processed
, USER_HOME_STRING
,
379 strlen(USER_HOME_STRING
))==0 && config
->user_dir
)
381 if(User_main(cr
, s_request
)!=0)
385 /* Handling method requested */
386 if(s_request
->method
==HTTP_METHOD_POST
)
388 if((status
=mk_method_post(cr
, s_request
))==-1){
393 status
= mk_http_init(cr
, s_request
);
398 /* Return a struct with method, URI , protocol version
399 and all static headers defined here sent in request */
400 int mk_request_header_process(struct request
*sr
)
402 int uri_init
=0, uri_end
=0;
404 int prot_init
=0, prot_end
=0, pos_sep
=0;
406 char *str_prot
=0, *port
=0;
410 /* If verification fails it will return always
411 * a bad request status
413 sr
->log
->final_response
= M_CLIENT_BAD_REQUEST
;
416 sr
->method_p
= mk_http_method_check_str(sr
->method
);
419 uri_init
= (index(sr
->body
.data
, ' ') - sr
->body
.data
) + 1;
420 fh_limit
= (index(sr
->body
.data
, '\n') - sr
->body
.data
);
422 uri_end
= mk_string_search_r(sr
->body
.data
, ' ',
430 prot_init
= uri_end
+ 2;
432 if(uri_end
< uri_init
)
438 query_init
= index(sr
->body
.data
+uri_init
, '?');
443 init
= (int) (query_init
-(sr
->body
.data
+uri_init
)) + uri_init
;
449 sr
->query_string
= mk_pointer_create(sr
->body
.data
,
454 /* Request URI Part 2 */
455 sr
->uri
= sr
->log
->uri
= mk_pointer_create(sr
->body
.data
,
456 uri_init
, uri_end
+1);
465 prot_end
= fh_limit
-1;
466 if(prot_end
!=prot_init
&& prot_end
>0){
467 str_prot
= mk_string_copy_substr(sr
->body
.data
,
468 prot_init
, prot_end
);
469 sr
->protocol
= sr
->log
->protocol
=
470 mk_http_protocol_check(str_prot
);
472 mk_mem_free(str_prot
);
475 headers
= sr
->body
.data
+prot_end
+mk_crlf
.len
;
478 sr
->uri_processed
= get_real_string(sr
->uri
);
480 if(!sr
->uri_processed
)
482 sr
->uri_processed
= mk_pointer_to_buf(sr
->uri
);
483 sr
->uri_twin
= VAR_ON
;
486 /* Creating table of content (index) for request headers */
487 int toc_len
= MK_KNOWN_HEADERS
;
488 struct header_toc
*toc
= mk_request_header_toc_create(toc_len
);
489 mk_request_header_toc_parse(toc
, headers
, toc_len
);
492 host
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_host
);
496 if((pos_sep
= mk_string_char_search(host
.data
, ':', host
.len
))>=0)
498 sr
->host
.data
= host
.data
;
499 sr
->host
.len
= pos_sep
;
501 port
= mk_string_copy_substr(host
.data
, pos_sep
+1, host
.len
);
502 sr
->port
= atoi(port
);
506 sr
->host
=host
; /* maybe null */
507 sr
->port
=config
->standard_port
;
514 /* Looking for headers */
515 sr
->accept
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_accept
);
516 sr
->accept_charset
= mk_request_header_find(toc
, toc_len
, headers
,
517 mk_rh_accept_charset
);
518 sr
->accept_encoding
= mk_request_header_find(toc
, toc_len
, headers
,
519 mk_rh_accept_encoding
);
522 sr
->accept_language
= mk_request_header_find(toc
, toc_len
, headers
,
523 mk_rh_accept_language
);
524 sr
->cookies
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_cookie
);
525 sr
->connection
= mk_request_header_find(toc
, toc_len
, headers
,
527 sr
->referer
= mk_request_header_find(toc
, toc_len
, headers
,
529 sr
->user_agent
= mk_request_header_find(toc
, toc_len
, headers
,
531 sr
->range
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_range
);
532 sr
->if_modified_since
= mk_request_header_find(toc
, toc_len
, headers
,
533 mk_rh_if_modified_since
);
535 /* Checking keepalive */
536 sr
->keep_alive
=VAR_OFF
;
537 if(sr
->connection
.data
)
539 if(sr
->protocol
==HTTP_PROTOCOL_11
||
540 sr
->protocol
==HTTP_PROTOCOL_10
)
542 if(mk_string_casestr(sr
->connection
.data
,"Keep-Alive"))
544 sr
->keep_alive
=VAR_ON
;
549 sr
->log
->final_response
= M_HTTP_OK
;
552 mk_pointer_print(sr->method_p);
553 mk_pointer_print(sr->uri);
554 mk_pointer_print(sr->query_string);
560 /* Return value of some variable sent in request */
561 mk_pointer
mk_request_header_find(struct header_toc
*toc
, int toc_len
,
562 char *request_body
, mk_pointer header
)
573 for(i
=0; i
<toc_len
; i
++)
575 if(toc
[i
].status
== 1)
583 if(strncasecmp(toc
[i
].init
, header
.data
, header
.len
)==0)
585 var
.data
= toc
[i
].init
+ header
.len
+ 1;
586 var
.len
= toc
[i
].end
- var
.data
;
596 /* FIXME: IMPROVE access */
597 /* Look for some index.xxx in pathfile */
598 mk_pointer
mk_request_index(char *pathfile
)
603 struct indexfile
*aux_index
;
605 mk_pointer_reset(&f
);
607 aux_index
=first_index
;
610 m_build_buffer(&file_aux
, &len
, "%s%s",
611 pathfile
, aux_index
->indexname
);
613 if(access(file_aux
,F_OK
)==0)
619 mk_mem_free(file_aux
);
620 aux_index
=aux_index
->next
;
626 /* Send error responses */
627 void mk_request_error(int num_error
, struct client_request
*cr
,
628 struct request
*s_request
, int debug
, struct log_info
*s_log
)
631 mk_pointer message
, page
;
635 s_log
=mk_mem_malloc(sizeof(struct log_info
));
638 mk_pointer_reset(&page
);
641 case M_CLIENT_BAD_REQUEST
:
642 mk_request_set_default_page(&page
, "Bad Request",
644 s_request
->host_conf
->host_signature
);
645 s_log
->error_msg
= request_error_msg_400
;
648 case M_CLIENT_FORBIDDEN
:
649 mk_request_set_default_page(&page
, "Forbidden",
651 s_request
->host_conf
->host_signature
);
652 s_log
->error_msg
= request_error_msg_403
;
653 // req s_request->uri;
656 case M_CLIENT_NOT_FOUND
:
657 m_build_buffer(&message
.data
, &message
.len
,
658 "The requested URL was not found on this server.");
659 mk_request_set_default_page(&page
, "Not Found",
661 s_request
->host_conf
->host_signature
);
662 s_log
->error_msg
= request_error_msg_404
;
664 mk_pointer_free(&message
);
667 case M_CLIENT_METHOD_NOT_ALLOWED
:
668 mk_request_set_default_page(&page
, "Method Not Allowed",
670 s_request
->host_conf
->host_signature
);
672 s_log
->final_response
=M_CLIENT_METHOD_NOT_ALLOWED
;
673 s_log
->error_msg
= request_error_msg_405
;
676 case M_CLIENT_REQUEST_TIMEOUT
:
677 s_log
->status
=S_LOG_OFF
;
678 s_log
->error_msg
= request_error_msg_408
;
681 case M_CLIENT_LENGTH_REQUIRED
:
682 s_log
->error_msg
= request_error_msg_411
;
685 case M_SERVER_NOT_IMPLEMENTED
:
686 mk_request_set_default_page(&page
,
687 "Method Not Implemented",
689 s_request
->host_conf
->host_signature
);
690 s_log
->final_response
=M_SERVER_NOT_IMPLEMENTED
;
691 s_log
->error_msg
= request_error_msg_501
;
694 case M_SERVER_INTERNAL_ERROR
:
695 m_build_buffer(&message
.data
, &message
.len
,
696 "Problems found running %s ",
698 mk_request_set_default_page(&page
, "Internal Server Error",
699 message
, s_request
->host_conf
->host_signature
);
700 s_log
->error_msg
= request_error_msg_500
;
702 mk_pointer_free(&message
);
705 case M_SERVER_HTTP_VERSION_UNSUP
:
706 mk_pointer_reset(&message
);
707 mk_request_set_default_page(&page
,
708 "HTTP Version Not Supported",
710 s_request
->host_conf
->host_signature
);
711 s_log
->error_msg
= request_error_msg_505
;
715 s_log
->final_response
=num_error
;
717 s_request
->headers
->status
= num_error
;
718 s_request
->headers
->content_length
= page
.len
;
719 s_request
->headers
->content_length_p
= mk_utils_int2mkp(page
.len
);
720 s_request
->headers
->location
= NULL
;
721 s_request
->headers
->cgi
= SH_NOCGI
;
722 s_request
->headers
->pconnections_left
= 0;
723 mk_pointer_reset(&s_request
->headers
->last_modified
);
725 if(aux_message
) mk_mem_free(aux_message
);
729 mk_pointer_reset(&s_request
->headers
->content_type
);
733 mk_pointer_set(&s_request
->headers
->content_type
, "text/html");
736 mk_header_send(cr
->socket
, cr
, s_request
, s_log
);
739 n
= write(cr
->socket
, page
.data
, page
.len
);
740 mk_pointer_free(&page
);
744 /* Build error page */
745 void mk_request_set_default_page(mk_pointer
*page
, char *title
,
746 mk_pointer message
, char *signature
)
750 temp
= mk_pointer_to_buf(message
);
751 m_build_buffer(&page
->data
, &page
->len
,
752 MK_REQUEST_DEFAULT_PAGE
,
753 title
, temp
, signature
);
757 /* Create a memory allocation in order to handle the request data */
758 struct request
*mk_request_alloc()
760 struct request
*request
=0;
762 request
= (struct request
*) mk_mem_malloc(sizeof(struct request
));
763 request
->log
= (struct log_info
*) mk_mem_malloc(sizeof(struct log_info
));
765 request
->status
=VAR_OFF
; /* Request not processed yet */
766 request
->make_log
=VAR_ON
; /* build log file of this request ? */
768 mk_pointer_reset(&request
->body
);
770 request
->log
->final_response
=M_HTTP_OK
;
771 request
->log
->status
=S_LOG_ON
;
773 mk_pointer_reset(&request
->log
->size_p
);
774 mk_pointer_reset(&request
->log
->error_msg
);
776 request
->status
=VAR_ON
;
777 request
->method
=METHOD_NOT_FOUND
;
779 mk_pointer_reset(&request
->uri
);
780 request
->uri_processed
= NULL
;
781 request
->uri_twin
= VAR_OFF
;
783 request
->accept
.data
= NULL
;
784 request
->accept_language
.data
= NULL
;
785 request
->accept_encoding
.data
= NULL
;
786 request
->accept_charset
.data
= NULL
;
787 request
->content_type
.data
= NULL
;
788 request
->connection
.data
= NULL
;
789 request
->cookies
.data
= NULL
;
790 request
->host
.data
= NULL
;
791 request
->if_modified_since
.data
= NULL
;
792 request
->last_modified_since
.data
= NULL
;
793 request
->range
.data
= NULL
;
794 request
->referer
.data
= NULL
;
795 request
->resume
.data
= NULL
;
796 request
->user_agent
.data
= NULL
;
798 request
->post_variables
.data
= NULL
;
800 request
->user_uri
= NULL
;
801 mk_pointer_reset(&request
->query_string
);
803 request
->virtual_user
= NULL
;
804 request
->script_filename
= NULL
;
805 mk_pointer_reset(&request
->real_path
);
806 request
->host_conf
= config
->hosts
;
808 request
->bytes_to_send
= -1;
809 request
->bytes_offset
= 0;
810 request
->fd_file
= -1;
812 /* Response Headers */
813 request
->headers
= mk_header_create();
815 return (struct request
*) request
;
818 void mk_request_free_list(struct client_request
*cr
)
820 struct request
*sr
=0, *before
=0;
826 sr
= before
= cr
->request
;
834 while(before
->next
!=sr
){
835 before
= before
->next
;
847 void mk_request_free(struct request
*sr
)
849 /* I hate it, but I don't know another light way :( */
855 mk_mem_free(sr
->headers
->location
);
856 mk_pointer_free(&sr
->headers
->last_modified
);
858 mk_mem_free(sr->headers->content_type);
859 headers->content_type never it's allocated
860 with malloc or something, so we don't need
861 to free it, the value has been freed before
862 in M_METHOD_Get_and_Head(struct request *sr)
864 this BUG was reported by gentoo team.. thanks guys XD
867 mk_mem_free(sr
->headers
);
872 //mk_mem_free(sr->log->error_msg);
873 mk_mem_free(sr
->log
);
876 mk_pointer_reset(&sr
->body
);
877 mk_pointer_reset(&sr
->uri
);
879 if(sr
->uri_twin
==VAR_ON
)
881 mk_mem_free(sr
->uri_processed
);
884 mk_pointer_free(&sr
->post_variables
);
885 mk_mem_free(sr
->user_uri
);
886 mk_pointer_reset(&sr
->query_string
);
888 mk_mem_free(sr
->virtual_user
);
889 mk_mem_free(sr
->script_filename
);
890 mk_pointer_free(&sr
->real_path
);
894 /* Create a client request struct and put it on the
897 struct client_request
*mk_request_client_create(int socket
)
899 struct request_idx
*request_index
;
900 struct client_request
*cr
;
902 cr
= mk_mem_malloc(sizeof(struct client_request
));
904 cr
->pipelined
= FALSE
;
905 cr
->counter_connections
= 0;
909 mk_pointer_set(&cr
->ip
, mk_socket_get_ip(socket
));
911 /* creation time in unix time */
912 // cr->connection_timeout = log_current_utime + config->timeout;
915 cr
->body
= mk_mem_malloc(MAX_REQUEST_BODY
);
917 cr
->first_block_end
= -1;
918 cr
->first_method
= HTTP_METHOD_UNKNOWN
;
920 request_index
= mk_sched_get_request_index();
921 if(!request_index
->first
)
923 request_index
->first
= request_index
->last
= cr
;
924 mk_sched_set_request_index(request_index
);
927 request_index
->last
->next
= cr
;
928 request_index
->last
= cr
;
931 return (struct client_request
*) cr
;
934 struct client_request
*mk_request_client_get(int socket
)
936 struct request_idx
*request_index
;
937 struct client_request
*cr
=NULL
;
939 request_index
= mk_sched_get_request_index();
940 cr
= request_index
->first
;
943 if(cr
->socket
== socket
)
950 return (struct client_request
*) cr
;
954 * From thread sched_list_node "list", remove the client_request
957 struct client_request
*mk_request_client_remove(int socket
)
959 struct request_idx
*request_index
;
960 struct client_request
*cr
, *aux
;
962 request_index
= mk_sched_get_request_index();
963 cr
= request_index
->first
;
967 if(cr
->socket
== socket
)
969 if(cr
==request_index
->first
)
971 request_index
->first
= cr
->next
;
975 aux
= request_index
->first
;
980 aux
->next
= cr
->next
;
983 request_index
->last
= aux
;
991 mk_pointer_free(&cr
->ip
);
992 mk_mem_free(cr
->body
);
998 struct header_toc
*mk_request_header_toc_create(int len
)
1001 struct header_toc
*p
;
1003 p
= (struct header_toc
*) pthread_getspecific(mk_cache_header_toc
);
1005 for(i
=0; i
<len
; i
++)
1014 void mk_request_header_toc_parse(struct header_toc
*toc
, char *data
, int len
)
1020 for(i
=0; i
<len
&& p
; i
++)
1022 l
= strstr(p
, MK_CRLF
);
1029 p
= l
+ mk_crlf
.len
;