1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
5 * Copyright (C) 2001-2008, Eduardo Silva P.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <sys/socket.h>
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39 #include <sys/types.h>
44 #include "http_status.h"
48 #include "scheduler.h"
62 struct request
*mk_request_parse(struct client_request
*cr
)
64 int i
, n
, init_block
= 0, n_blocks
= 0;
65 int pipelined
= FALSE
;
66 struct request
*cr_buf
= 0, *cr_search
= 0;
70 for (i
= cr
->first_block_end
; i
<= cr
->body_length
- mk_endblock
.len
; i
++) {
71 /* Allocating request block */
72 cr_buf
= mk_request_alloc();
75 cr_buf
->body
.data
= cr
->body
+ init_block
;
76 cr_buf
->body
.len
= i
- init_block
;
78 if (i
== cr
->first_block_end
) {
79 cr_buf
->method
= cr
->first_method
;
82 cr_buf
->method
= mk_http_method_get(cr_buf
->body
.data
);
87 i
= init_block
= i
+ mk_endblock
.len
;
89 /* Looking for POST data */
90 if (cr_buf
->method
== HTTP_METHOD_POST
) {
91 cr_buf
->post_variables
= mk_method_post_get_vars(cr
->body
, i
);
93 if (cr_buf
->post_variables
.len
>= 0) {
94 i
= init_block
= i
+ cr_buf
->post_variables
.len
;
102 cr_search
= cr
->request
;
104 if (cr_search
->next
== NULL
) {
105 cr_search
->next
= cr_buf
;
109 cr_search
= cr_search
->next
;
114 n
= mk_string_search(cr
->body
+ i
, mk_endblock
.data
);
123 /* Checking pipelining connection */
124 cr_search
= cr
->request
;
129 if (cr_search
->method
!= HTTP_METHOD_GET
&&
130 cr_search
->method
!= HTTP_METHOD_HEAD
) {
134 cr_search
= cr_search
->next
;
137 if (pipelined
== FALSE
) {
138 /* All pipelined requests must use GET method */
142 cr
->pipelined
= TRUE
;
147 printf("*****************************************");
149 cr_search = cr->request;
151 printf("\n---BLOCK---:\n%s---END BLOCK---\n\n", cr_search->body);
153 cr_search = cr_search->next;
160 int mk_handler_read(int socket
, struct client_request
*cr
)
164 bytes
= read(socket
, cr
->body
+ cr
->body_length
,
165 MAX_REQUEST_BODY
- cr
->body_length
);
168 if (errno
== EAGAIN
) {
172 mk_request_client_remove(socket
);
177 mk_request_client_remove(socket
);
182 cr
->body_length
+= bytes
;
183 cr
->body
[cr
->body_length
] = '\0';
189 int mk_handler_write(int socket
, struct client_request
*cr
)
191 int bytes
, final_status
= 0;
195 * Get node from schedule list node which contains
196 * the information regarding to the current thread
203 if (!mk_request_parse(cr
)) {
211 /* Request not processed also no plugin has take some action */
212 if (sr
->bytes_to_send
< 0 && !sr
->handled_by
) {
213 final_status
= mk_request_process(cr
, sr
);
215 /* Request with data to send by static file sender */
216 else if (sr
->bytes_to_send
> 0 && !sr
->handled_by
) {
217 bytes
= SendFile(socket
, cr
, sr
);
218 final_status
= bytes
;
220 else if (sr
->handled_by
){
221 /* FIXME: Look for loops
226 * If we got an error, we don't want to parse
227 * and send information for another pipelined request
229 if (final_status
> 0) {
232 else if (final_status
<= 0) {
233 mk_logger_write_log(cr
, sr
->log
, sr
->host_conf
);
239 /* If we are here, is because all pipelined request were
240 * processed successfully, let's return 0;
245 int mk_request_process(struct client_request
*cr
, struct request
*s_request
)
250 status
= mk_request_header_process(s_request
);
256 switch (s_request
->method
) {
257 case METHOD_NOT_ALLOWED
:
258 mk_request_error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
,
259 s_request
, 1, s_request
->log
);
261 case METHOD_NOT_FOUND
:
262 mk_request_error(M_SERVER_NOT_IMPLEMENTED
, cr
,
263 s_request
, 1, s_request
->log
);
267 s_request
->user_home
= VAR_OFF
;
268 s_request
->log
->method
= s_request
->method
;
270 /* Valid request URI? */
271 if (s_request
->uri_processed
== NULL
) {
272 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
, 1,
277 /* HTTP/1.1 needs Host header */
278 if (!s_request
->host
.data
&& s_request
->protocol
== HTTP_PROTOCOL_11
) {
279 s_request
->log
->final_response
= M_CLIENT_BAD_REQUEST
;
280 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
, 1,
285 /* Method not allowed ? */
286 if (s_request
->method
== METHOD_NOT_ALLOWED
) {
287 s_request
->log
->final_response
= M_CLIENT_METHOD_NOT_ALLOWED
;
288 mk_request_error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
, s_request
, 1,
293 /* Validating protocol version */
294 if (s_request
->protocol
== HTTP_PROTOCOL_UNKNOWN
) {
296 s_request
->log
->final_response
= M_SERVER_HTTP_VERSION_UNSUP
;
297 mk_request_error(M_SERVER_HTTP_VERSION_UNSUP
, cr
, s_request
, 1,
302 if (s_request
->host
.data
) {
303 host
= mk_config_host_find(s_request
->host
);
305 s_request
->host_conf
= host
;
308 s_request
->host_conf
= config
->hosts
;
312 s_request
->host_conf
= config
->hosts
;
314 s_request
->log
->host_conf
= s_request
->host_conf
;
316 /* is requesting an user home directory ? */
317 if (config
->user_dir
) {
318 if (strncmp(s_request
->uri_processed
,
319 mk_user_home
.data
, mk_user_home
.len
) == 0) {
320 if (mk_user_init(cr
, s_request
) != 0) {
326 /* Handling method requested */
327 if (s_request
->method
== HTTP_METHOD_POST
) {
328 if ((status
= mk_method_post(cr
, s_request
)) == -1) {
333 status
= mk_http_init(cr
, s_request
);
336 MK_TRACE("HTTP Init returning %i", status
);
342 /* Return a struct with method, URI , protocol version
343 and all static headers defined here sent in request */
344 int mk_request_header_process(struct request
*sr
)
346 int uri_init
= 0, uri_end
= 0;
347 char *query_init
= 0;
348 int prot_init
= 0, prot_end
= 0, pos_sep
= 0;
354 /* If verification fails it will return always
355 * a bad request status
357 sr
->log
->final_response
= M_CLIENT_BAD_REQUEST
;
360 sr
->method_p
= mk_http_method_check_str(sr
->method
);
363 uri_init
= (index(sr
->body
.data
, ' ') - sr
->body
.data
) + 1;
364 fh_limit
= (index(sr
->body
.data
, '\n') - sr
->body
.data
);
366 uri_end
= mk_string_search_r(sr
->body
.data
, ' ', fh_limit
) - 1;
372 prot_init
= uri_end
+ 2;
374 if (uri_end
< uri_init
) {
379 query_init
= index(sr
->body
.data
+ uri_init
, '?');
383 init
= (int) (query_init
- (sr
->body
.data
+ uri_init
)) + uri_init
;
384 if (init
<= uri_end
) {
388 sr
->query_string
= mk_pointer_create(sr
->body
.data
,
393 /* Request URI Part 2 */
394 sr
->uri
= sr
->log
->uri
= mk_pointer_create(sr
->body
.data
,
395 uri_init
, uri_end
+ 1);
397 if (sr
->uri
.len
< 1) {
403 prot_end
= fh_limit
- 1;
404 if (prot_end
!= prot_init
&& prot_end
> 0) {
405 sr
->protocol
= sr
->log
->protocol
=
406 mk_http_protocol_check(sr
->body
.data
+ prot_init
,
407 prot_end
- prot_init
);
410 headers
= sr
->body
.data
+ prot_end
+ mk_crlf
.len
;
413 sr
->uri_processed
= get_real_string(sr
->uri
);
415 if (!sr
->uri_processed
) {
416 sr
->uri_processed
= mk_pointer_to_buf(sr
->uri
);
417 sr
->uri_twin
= VAR_ON
;
420 /* Creating table of content (index) for request headers */
421 int toc_len
= MK_KNOWN_HEADERS
;
422 struct header_toc
*toc
= mk_request_header_toc_create(toc_len
);
423 mk_request_header_toc_parse(toc
, headers
, toc_len
);
426 host
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_host
);
429 if ((pos_sep
= mk_string_char_search(host
.data
, ':', host
.len
)) >= 0) {
430 sr
->host
.data
= host
.data
;
431 sr
->host
.len
= pos_sep
;
433 port
= mk_string_copy_substr(host
.data
, pos_sep
+ 1, host
.len
);
434 sr
->port
= atoi(port
);
438 sr
->host
= host
; /* maybe null */
439 sr
->port
= config
->standard_port
;
443 sr
->host
.data
= NULL
;
446 /* Looking for headers */
447 sr
->accept
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_accept
);
448 sr
->accept_charset
= mk_request_header_find(toc
, toc_len
, headers
,
449 mk_rh_accept_charset
);
450 sr
->accept_encoding
= mk_request_header_find(toc
, toc_len
, headers
,
451 mk_rh_accept_encoding
);
454 sr
->accept_language
= mk_request_header_find(toc
, toc_len
, headers
,
455 mk_rh_accept_language
);
456 sr
->cookies
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_cookie
);
457 sr
->connection
= mk_request_header_find(toc
, toc_len
, headers
,
459 sr
->referer
= mk_request_header_find(toc
, toc_len
, headers
,
461 sr
->user_agent
= mk_request_header_find(toc
, toc_len
, headers
,
463 sr
->range
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_range
);
464 sr
->if_modified_since
= mk_request_header_find(toc
, toc_len
, headers
,
465 mk_rh_if_modified_since
);
467 /* Default Keepalive is off */
468 sr
->keep_alive
= VAR_OFF
;
469 if (sr
->connection
.data
) {
470 if (mk_string_casestr(sr
->connection
.data
, "Keep-Alive")) {
471 sr
->keep_alive
= VAR_ON
;
475 /* Default value for HTTP/1.1 */
476 if (sr
->protocol
== HTTP_PROTOCOL_11
) {
477 /* Assume keep-alive connection */
478 sr
->keep_alive
= VAR_ON
;
481 sr
->log
->final_response
= M_HTTP_OK
;
486 /* Return value of some variable sent in request */
487 mk_pointer
mk_request_header_find(struct header_toc
* toc
, int toc_len
,
488 char *request_body
, mk_pointer header
)
498 for (i
= 0; i
< toc_len
; i
++) {
499 /* status = 1 means that the toc entry was already
502 if (toc
[i
].status
== 1) {
509 if (strncasecmp(toc
[i
].init
, header
.data
, header
.len
) == 0) {
510 var
.data
= toc
[i
].init
+ header
.len
+ 1;
511 var
.len
= toc
[i
].end
- var
.data
;
521 /* FIXME: IMPROVE access */
522 /* Look for some index.xxx in pathfile */
523 mk_pointer
mk_request_index(char *pathfile
)
528 struct indexfile
*aux_index
;
530 mk_pointer_reset(&f
);
532 aux_index
= first_index
;
535 m_build_buffer(&file_aux
, &len
, "%s%s",
536 pathfile
, aux_index
->indexname
);
538 if (access(file_aux
, F_OK
) == 0) {
543 mk_mem_free(file_aux
);
544 aux_index
= aux_index
->next
;
550 /* Send error responses */
551 void mk_request_error(int num_error
, struct client_request
*cr
,
552 struct request
*s_request
, int debug
,
553 struct log_info
*s_log
)
555 char *aux_message
= 0;
556 mk_pointer message
, *page
= 0;
560 case M_CLIENT_BAD_REQUEST
:
561 page
= mk_request_set_default_page("Bad Request",
563 s_request
->host_conf
->
565 s_log
->error_msg
= request_error_msg_400
;
568 case M_CLIENT_FORBIDDEN
:
569 page
= mk_request_set_default_page("Forbidden",
571 s_request
->host_conf
->
573 s_log
->error_msg
= request_error_msg_403
;
574 s_log
->error_details
= s_request
->uri
;
577 case M_CLIENT_NOT_FOUND
:
578 m_build_buffer(&message
.data
, &message
.len
,
579 "The requested URL was not found on this server.");
580 page
= mk_request_set_default_page("Not Found",
582 s_request
->host_conf
->
584 s_log
->error_msg
= request_error_msg_404
;
585 s_log
->error_details
= s_request
->uri
;
587 mk_pointer_free(&message
);
590 case M_CLIENT_METHOD_NOT_ALLOWED
:
591 page
= mk_request_set_default_page("Method Not Allowed",
593 s_request
->host_conf
->
596 s_log
->final_response
= M_CLIENT_METHOD_NOT_ALLOWED
;
597 s_log
->error_msg
= request_error_msg_405
;
598 s_log
->error_details
= s_request
->method_p
;
601 case M_CLIENT_REQUEST_TIMEOUT
:
602 s_log
->status
= S_LOG_OFF
;
603 s_log
->error_msg
= request_error_msg_408
;
606 case M_CLIENT_LENGTH_REQUIRED
:
607 s_log
->error_msg
= request_error_msg_411
;
610 case M_SERVER_NOT_IMPLEMENTED
:
611 page
= mk_request_set_default_page("Method Not Implemented",
613 s_request
->host_conf
->
615 s_log
->final_response
= M_SERVER_NOT_IMPLEMENTED
;
616 s_log
->error_msg
= request_error_msg_501
;
617 s_log
->error_details
= s_request
->method_p
;
620 case M_SERVER_INTERNAL_ERROR
:
621 m_build_buffer(&message
.data
, &message
.len
,
622 "Problems found running %s ", s_request
->uri
);
623 page
= mk_request_set_default_page("Internal Server Error",
625 s_request
->host_conf
->
627 s_log
->error_msg
= request_error_msg_500
;
629 mk_pointer_free(&message
);
632 case M_SERVER_HTTP_VERSION_UNSUP
:
633 mk_pointer_reset(&message
);
634 page
= mk_request_set_default_page("HTTP Version Not Supported",
636 s_request
->host_conf
->
638 s_log
->error_msg
= request_error_msg_505
;
642 s_log
->final_response
= num_error
;
644 s_request
->headers
->status
= num_error
;
646 s_request
->headers
->content_length
= page
->len
;
647 s_request
->headers
->content_length_p
= mk_utils_int2mkp(page
->len
);
650 s_request
->headers
->location
= NULL
;
651 s_request
->headers
->cgi
= SH_NOCGI
;
652 s_request
->headers
->pconnections_left
= 0;
653 mk_pointer_reset(&s_request
->headers
->last_modified
);
656 mk_mem_free(aux_message
);
659 mk_pointer_reset(&s_request
->headers
->content_type
);
662 mk_pointer_set(&s_request
->headers
->content_type
, "text/html");
665 mk_header_send(cr
->socket
, cr
, s_request
, s_log
);
668 n
= write(cr
->socket
, page
->data
, page
->len
);
669 mk_pointer_free(page
);
674 /* Build error page */
675 mk_pointer
*mk_request_set_default_page(char *title
, mk_pointer message
,
681 p
= mk_mem_malloc(sizeof(mk_pointer
));
683 temp
= mk_pointer_to_buf(message
);
684 m_build_buffer(&p
->data
, &p
->len
,
685 MK_REQUEST_DEFAULT_PAGE
, title
, temp
, signature
);
691 /* Create a memory allocation in order to handle the request data */
692 struct request
*mk_request_alloc()
694 struct request
*request
= 0;
696 request
= mk_mem_malloc(sizeof(struct request
));
697 request
->log
= mk_mem_malloc(sizeof(struct log_info
));
699 request
->status
= VAR_OFF
; /* Request not processed yet */
700 request
->make_log
= VAR_ON
; /* build log file of this request ? */
702 mk_pointer_reset(&request
->body
);
704 request
->log
->final_response
= M_HTTP_OK
;
705 request
->log
->status
= S_LOG_ON
;
706 mk_pointer_reset(&request
->log
->size_p
);
707 mk_pointer_reset(&request
->log
->error_msg
);
709 request
->status
= VAR_ON
;
710 request
->method
= METHOD_NOT_FOUND
;
712 mk_pointer_reset(&request
->uri
);
713 request
->uri_processed
= NULL
;
714 request
->uri_twin
= VAR_OFF
;
716 request
->accept
.data
= NULL
;
717 request
->accept_language
.data
= NULL
;
718 request
->accept_encoding
.data
= NULL
;
719 request
->accept_charset
.data
= NULL
;
720 request
->content_length
= 0;
721 request
->content_type
.data
= NULL
;
722 request
->connection
.data
= NULL
;
723 request
->cookies
.data
= NULL
;
724 request
->host
.data
= NULL
;
725 request
->if_modified_since
.data
= NULL
;
726 request
->last_modified_since
.data
= NULL
;
727 request
->range
.data
= NULL
;
728 request
->referer
.data
= NULL
;
729 request
->resume
.data
= NULL
;
730 request
->user_agent
.data
= NULL
;
732 request
->post_variables
.data
= NULL
;
734 request
->user_uri
= NULL
;
735 mk_pointer_reset(&request
->query_string
);
737 request
->file_info
= NULL
;
738 request
->virtual_user
= NULL
;
739 request
->script_filename
= NULL
;
740 mk_pointer_reset(&request
->real_path
);
741 request
->host_conf
= config
->hosts
;
744 request
->bytes_to_send
= -1;
745 request
->bytes_offset
= 0;
746 request
->fd_file
= -1;
748 /* Response Headers */
749 request
->headers
= mk_header_create();
751 request
->handled_by
= NULL
;
755 void mk_request_free_list(struct client_request
*cr
)
757 struct request
*sr
= 0, *before
= 0;
761 while (cr
->request
) {
762 sr
= before
= cr
->request
;
768 if (sr
!= cr
->request
) {
769 while (before
->next
!= sr
) {
770 before
= before
->next
;
782 void mk_request_free(struct request
*sr
)
784 /* I hate it, but I don't know another light way :( */
785 if (sr
->fd_file
> 0) {
789 mk_mem_free(sr
->headers
->location
);
790 mk_pointer_free(&sr
->headers
->content_length_p
);
791 mk_pointer_free(&sr
->headers
->last_modified
);
793 mk_mem_free(sr->headers->content_type);
794 headers->content_type never it's allocated
795 with malloc or something, so we don't need
796 to free it, the value has been freed before
797 in M_METHOD_Get_and_Head(struct request *sr)
799 this BUG was reported by gentoo team.. thanks guys XD
802 mk_mem_free(sr
->headers
);
808 * We do not free log->size_p, as if it was
809 * used due to an error, it points to the
810 * same memory block than header->content_length_p
811 * points to, we just reset it.
813 mk_pointer_reset(&sr
->log
->size_p
);
816 * sr->log->error_msg just point to
817 * local data on request.c, no
818 * dynamic allocation is made
821 mk_mem_free(sr
->log
);
824 mk_pointer_reset(&sr
->body
);
825 mk_pointer_reset(&sr
->uri
);
827 if (sr
->uri_twin
== VAR_ON
) {
828 mk_mem_free(sr
->uri_processed
);
831 mk_pointer_free(&sr
->post_variables
);
832 mk_mem_free(sr
->user_uri
);
833 mk_pointer_reset(&sr
->query_string
);
835 mk_mem_free(sr
->file_info
);
836 mk_mem_free(sr
->virtual_user
);
837 mk_mem_free(sr
->script_filename
);
838 mk_pointer_free(&sr
->real_path
);
842 /* Create a client request struct and put it on the
845 struct client_request
*mk_request_client_create(int socket
)
847 struct request_idx
*request_index
;
848 struct client_request
*cr
;
849 struct sched_connection
*sc
;
851 sc
= mk_sched_get_connection(NULL
, socket
);
852 cr
= mk_mem_malloc(sizeof(struct client_request
));
855 cr
->ipv4
= &sc
->ipv4
;
857 cr
->pipelined
= FALSE
;
858 cr
->counter_connections
= 0;
860 cr
->status
= MK_REQUEST_STATUS_INCOMPLETE
;
863 /* creation time in unix time */
864 cr
->init_time
= sc
->arrive_time
;
867 cr
->body
= mk_mem_malloc(MAX_REQUEST_BODY
);
869 cr
->first_block_end
= -1;
870 cr
->first_method
= HTTP_METHOD_UNKNOWN
;
872 request_index
= mk_sched_get_request_index();
873 if (!request_index
->first
) {
874 request_index
->first
= request_index
->last
= cr
;
877 request_index
->last
->next
= cr
;
878 request_index
->last
= cr
;
880 mk_sched_set_request_index(request_index
);
883 mk_sched_update_thread_status(NULL
,
884 MK_SCHEDULER_ACTIVE_UP
,
885 MK_SCHEDULER_CLOSED_NONE
);
890 struct client_request
*mk_request_client_get(int socket
)
892 struct request_idx
*request_index
;
893 struct client_request
*cr
= NULL
;
895 request_index
= mk_sched_get_request_index();
896 cr
= request_index
->first
;
898 if (cr
->socket
== socket
) {
908 * From thread sched_list_node "list", remove the client_request
911 struct client_request
*mk_request_client_remove(int socket
)
913 struct request_idx
*request_index
;
914 struct client_request
*cr
, *aux
;
916 request_index
= mk_sched_get_request_index();
917 cr
= request_index
->first
;
920 if (cr
->socket
== socket
) {
921 if (cr
== request_index
->first
) {
922 request_index
->first
= cr
->next
;
925 aux
= request_index
->first
;
926 while (aux
->next
!= cr
) {
929 aux
->next
= cr
->next
;
931 request_index
->last
= aux
;
939 /* No keep alive connection */
940 if (cr
->counter_connections
== 0) {
941 mk_sched_update_thread_status(NULL
,
942 MK_SCHEDULER_ACTIVE_DOWN
,
943 MK_SCHEDULER_CLOSED_UP
);
945 //mk_pointer_free(&cr->ip);
946 mk_mem_free(cr
->body
);
952 struct header_toc
*mk_request_header_toc_create(int len
)
955 struct header_toc
*p
;
957 p
= (struct header_toc
*) pthread_getspecific(mk_cache_header_toc
);
959 for (i
= 0; i
< len
; i
++) {
967 void mk_request_header_toc_parse(struct header_toc
*toc
, char *data
, int len
)
973 for (i
= 0; i
< len
&& p
; i
++) {
974 l
= strstr(p
, MK_CRLF
);
986 void mk_request_ka_next(struct client_request
*cr
)
988 bzero(cr
->body
, sizeof(cr
->body
));
989 cr
->first_method
= -1;
990 cr
->first_block_end
= -1;
992 cr
->counter_connections
++;