1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
5 * Copyright (C) 2001-2010, Eduardo Silva P. <edsiper@gmail.com>
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>
29 #include <sys/ioctl.h>
38 #include <arpa/inet.h>
39 #include <netinet/in.h>
40 #include <sys/types.h>
45 #include "http_status.h"
49 #include "scheduler.h"
63 struct request
*mk_request_parse(struct client_request
*cr
)
67 struct request
*cr_buf
= 0, *cr_search
= 0;
69 for (i
= 0; i
<= cr
->body_pos_end
; i
++) {
70 /* Look for CRLFCRLF (\r\n\r\n), maybe some pipelining
71 * request can be involved.
73 end
= mk_string_search(cr
->body
+ i
, mk_endblock
.data
) + i
;
79 /* Allocating request block */
80 cr_buf
= mk_request_alloc();
82 /* We point the block with a mk_pointer */
83 cr_buf
->body
.data
= cr
->body
+ i
;
84 cr_buf
->body
.len
= end
- i
;
86 /* Method, previous catch in mk_http_pending_request */
88 cr_buf
->method
= cr
->first_method
;
91 cr_buf
->method
= mk_http_method_get(cr_buf
->body
.data
);
95 /* Looking for POST data */
96 if (cr_buf
->method
== HTTP_METHOD_POST
) {
97 cr_buf
->post_variables
= mk_method_post_get_vars(cr
->body
,
98 end
+ mk_endblock
.len
);
99 if (cr_buf
->post_variables
.len
>= 0) {
100 i
+= cr_buf
->post_variables
.len
;
104 /* Increase index to the end of the current block */
105 i
= (end
+ mk_endblock
.len
) - 1;
109 cr
->request
= cr_buf
;
112 cr_search
= cr
->request
;
114 if (cr_search
->next
== NULL
) {
115 cr_search
->next
= cr_buf
;
119 cr_search
= cr_search
->next
;
130 cr_search = cr->request;
133 MK_TRACE("BLOCK INIT");
134 mk_pointer_print(cr_search->body);
135 MK_TRACE("BLOCK_END");
137 cr_search = cr_search->next;
141 /* Checking pipelining connection */
142 cr_search
= cr
->request
;
145 /* Pipelining request must use GET or HEAD methods */
146 if (cr_search
->method
!= HTTP_METHOD_GET
&&
147 cr_search
->method
!= HTTP_METHOD_HEAD
) {
150 cr_search
= cr_search
->next
;
153 cr
->pipelined
= TRUE
;
159 int mk_handler_read(int socket
, struct client_request
*cr
)
168 /* Check amount of data reported */
169 ret
= ioctl(socket
, FIONREAD
, &pending
);
171 mk_request_client_remove(socket
);
175 /* Reallocate buffer size if pending data does not have space */
176 if (pending
> 0 && (pending
>= (cr
->body_size
- (cr
->body_length
- 1)))) {
177 /* check available space */
178 available
= (cr
->body_size
- cr
->body_length
) + MK_REQUEST_CHUNK
;
179 if (pending
< available
) {
180 new_size
= cr
->body_size
+ MK_REQUEST_CHUNK
+ 1;
183 new_size
= cr
->body_size
+ pending
+ 1;
186 if (new_size
> config
->max_request_size
) {
190 tmp
= mk_mem_realloc(cr
->body
, new_size
);
193 cr
->body_size
= new_size
;
196 mk_request_client_remove(socket
);
202 bytes
= mk_socket_read(socket
, cr
->body
+ cr
->body_length
,
203 (cr
->body_size
- cr
->body_length
) );
206 if (errno
== EAGAIN
) {
210 mk_request_client_remove(socket
);
215 mk_request_client_remove(socket
);
220 cr
->body_length
+= bytes
;
221 cr
->body
[cr
->body_length
] = '\0';
227 int mk_handler_write(int socket
, struct client_request
*cr
)
229 int bytes
, final_status
= 0;
233 * Get node from schedule list node which contains
234 * the information regarding to the current thread
241 if (!mk_request_parse(cr
)) {
248 /* Request not processed also no plugin has take some action */
249 if (sr
->bytes_to_send
< 0 && !sr
->handled_by
) {
250 final_status
= mk_request_process(cr
, sr
);
252 /* Request with data to send by static file sender */
253 else if (sr
->bytes_to_send
> 0 && !sr
->handled_by
) {
254 final_status
= bytes
= mk_http_send_file(cr
, sr
);
258 * If we got an error, we don't want to parse
259 * and send information for another pipelined request
261 if (final_status
> 0) {
264 else if (final_status
<= 0) {
265 /* STAGE_40, request has ended */
266 mk_plugin_stage_run(MK_PLUGIN_STAGE_40
, cr
->socket
,
268 switch (final_status
) {
271 if (sr
->close_now
== VAR_ON
) {
283 /* If we are here, is because all pipelined request were
284 * processed successfully, let's return 0;
289 int mk_request_process(struct client_request
*cr
, struct request
*s_request
)
294 status
= mk_request_header_process(s_request
);
296 mk_header_set_http_status(s_request
, M_CLIENT_BAD_REQUEST
);
297 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
);
301 switch (s_request
->method
) {
302 case METHOD_NOT_ALLOWED
:
303 mk_request_error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
, s_request
);
305 case METHOD_NOT_FOUND
:
306 mk_request_error(M_SERVER_NOT_IMPLEMENTED
, cr
, s_request
);
310 s_request
->user_home
= VAR_OFF
;
312 /* Valid request URI? */
313 if (s_request
->uri_processed
== NULL
) {
314 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
);
317 if (s_request
->uri_processed
[0] != '/') {
318 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
);
321 if (s_request
->uri_processed
[0] != '/') {
322 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
);
326 /* HTTP/1.1 needs Host header */
327 if (!s_request
->host
.data
&& s_request
->protocol
== HTTP_PROTOCOL_11
) {
328 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, s_request
);
332 /* Method not allowed ? */
333 if (s_request
->method
== METHOD_NOT_ALLOWED
) {
334 mk_request_error(M_CLIENT_METHOD_NOT_ALLOWED
, cr
, s_request
);
338 /* Validating protocol version */
339 if (s_request
->protocol
== HTTP_PROTOCOL_UNKNOWN
) {
340 mk_request_error(M_SERVER_HTTP_VERSION_UNSUP
, cr
, s_request
);
344 if (s_request
->host
.data
) {
345 host
= mk_config_host_find(s_request
->host
);
347 s_request
->host_conf
= host
;
350 s_request
->host_conf
= config
->hosts
;
354 s_request
->host_conf
= config
->hosts
;
357 /* is requesting an user home directory ? */
358 if (config
->user_dir
) {
359 if (strncmp(s_request
->uri_processed
,
360 mk_user_home
.data
, mk_user_home
.len
) == 0) {
361 if (mk_user_init(cr
, s_request
) != 0) {
367 /* Handling method requested */
368 if (s_request
->method
== HTTP_METHOD_POST
) {
369 if ((status
= mk_method_post(cr
, s_request
)) == -1) {
374 /* Plugins Stage 20 */
376 ret
= mk_plugin_stage_run(MK_PLUGIN_STAGE_20
, cr
->socket
, NULL
,
379 if (ret
== MK_PLUGIN_RET_CLOSE_CONX
) {
381 MK_TRACE("STAGE 20 requested close conexion");
386 /* Normal HTTP process */
387 status
= mk_http_init(cr
, s_request
);
390 MK_TRACE("HTTP Init returning %i", status
);
396 /* Return a struct with method, URI , protocol version
397 and all static headers defined here sent in request */
398 int mk_request_header_process(struct request
*sr
)
400 int uri_init
= 0, uri_end
= 0;
401 char *query_init
= 0;
402 int prot_init
= 0, prot_end
= 0, pos_sep
= 0;
409 sr
->method_p
= mk_http_method_check_str(sr
->method
);
412 uri_init
= (index(sr
->body
.data
, ' ') - sr
->body
.data
) + 1;
413 fh_limit
= (index(sr
->body
.data
, '\n') - sr
->body
.data
);
415 uri_end
= mk_string_search_r(sr
->body
.data
, ' ', fh_limit
) - 1;
419 MK_TRACE("Error, first header bad formed");
424 prot_init
= uri_end
+ 2;
426 if (uri_end
< uri_init
) {
431 query_init
= index(sr
->body
.data
+ uri_init
, '?');
435 init
= (int) (query_init
- (sr
->body
.data
+ uri_init
)) + uri_init
;
436 if (init
<= uri_end
) {
440 sr
->query_string
= mk_pointer_create(sr
->body
.data
,
445 /* Request URI Part 2 */
446 sr
->uri
= mk_pointer_create(sr
->body
.data
, uri_init
, uri_end
+ 1);
448 if (sr
->uri
.len
< 1) {
453 prot_end
= fh_limit
- 1;
454 if (prot_init
== prot_end
) {
458 if (prot_end
!= prot_init
&& prot_end
> 0) {
459 sr
->protocol
= mk_http_protocol_check(sr
->body
.data
+ prot_init
,
460 prot_end
- prot_init
);
461 sr
->protocol_p
= mk_http_protocol_check_str(sr
->protocol
);
464 headers
= sr
->body
.data
+ prot_end
+ mk_crlf
.len
;
467 sr
->uri_processed
= mk_utils_hexuri_to_ascii(sr
->uri
);
469 if (!sr
->uri_processed
) {
470 sr
->uri_processed
= mk_pointer_to_buf(sr
->uri
);
471 sr
->uri_twin
= VAR_ON
;
474 /* Creating table of content (index) for request headers */
475 int toc_len
= MK_KNOWN_HEADERS
;
476 int headers_len
= sr
->body
.len
- (prot_end
+ mk_crlf
.len
);
478 struct header_toc
*toc
= mk_request_header_toc_create(toc_len
);
479 mk_request_header_toc_parse(toc
, toc_len
, headers
, headers_len
);
482 host
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_host
);
485 if ((pos_sep
= mk_string_char_search(host
.data
, ':', host
.len
)) >= 0) {
486 sr
->host
.data
= host
.data
;
487 sr
->host
.len
= pos_sep
;
489 port
= mk_string_copy_substr(host
.data
, pos_sep
+ 1, host
.len
);
490 sr
->port
= atoi(port
);
494 sr
->host
= host
; /* maybe null */
495 sr
->port
= config
->standard_port
;
499 sr
->host
.data
= NULL
;
502 /* Looking for headers */
503 sr
->accept
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_accept
);
504 sr
->accept_charset
= mk_request_header_find(toc
, toc_len
, headers
,
505 mk_rh_accept_charset
);
506 sr
->accept_encoding
= mk_request_header_find(toc
, toc_len
, headers
,
507 mk_rh_accept_encoding
);
510 sr
->accept_language
= mk_request_header_find(toc
, toc_len
, headers
,
511 mk_rh_accept_language
);
512 sr
->cookies
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_cookie
);
513 sr
->connection
= mk_request_header_find(toc
, toc_len
, headers
,
515 sr
->referer
= mk_request_header_find(toc
, toc_len
, headers
,
517 sr
->user_agent
= mk_request_header_find(toc
, toc_len
, headers
,
519 sr
->range
= mk_request_header_find(toc
, toc_len
, headers
, mk_rh_range
);
520 sr
->if_modified_since
= mk_request_header_find(toc
, toc_len
, headers
,
521 mk_rh_if_modified_since
);
523 /* Default Keepalive is off */
524 if (sr
->protocol
== HTTP_PROTOCOL_10
) {
525 sr
->keep_alive
= VAR_OFF
;
526 sr
->close_now
= VAR_ON
;
528 else if(sr
->protocol
== HTTP_PROTOCOL_11
) {
529 sr
->keep_alive
= VAR_ON
;
530 sr
->close_now
= VAR_OFF
;
533 if (sr
->connection
.data
) {
534 if (mk_string_casestr(sr
->connection
.data
, "Keep-Alive")) {
535 sr
->keep_alive
= VAR_ON
;
536 sr
->close_now
= VAR_OFF
;
538 else if(mk_string_casestr(sr
->connection
.data
, "Close")) {
539 sr
->keep_alive
= VAR_OFF
;
540 sr
->close_now
= VAR_ON
;
543 /* Set as a non-valid connection header value */
544 sr
->connection
.len
= 0;
551 /* Return value of some variable sent in request */
552 mk_pointer
mk_request_header_find(struct header_toc
* toc
, int toc_len
,
553 char *request_body
, mk_pointer header
)
562 for (i
= 0; i
< toc_len
; i
++) {
563 /* status = 1 means that the toc entry was already
566 if (toc
[i
].status
== 1) {
573 if (strncasecmp(toc
[i
].init
, header
.data
, header
.len
) == 0) {
574 var
.data
= toc
[i
].init
+ header
.len
+ 1;
575 var
.len
= toc
[i
].end
- var
.data
;
585 /* Look for some index.xxx in pathfile */
586 mk_pointer
mk_request_index(char *pathfile
)
591 struct mk_string_line
*aux_index
;
593 mk_pointer_reset(&f
);
595 aux_index
= config
->index_files
;
598 mk_string_build(&file_aux
, &len
, "%s%s",
599 pathfile
, aux_index
->val
);
601 if (access(file_aux
, F_OK
) == 0) {
606 mk_mem_free(file_aux
);
607 aux_index
= aux_index
->next
;
613 /* Send error responses */
614 void mk_request_error(int http_status
, struct client_request
*cr
,
615 struct request
*sr
) {
616 char *aux_message
= 0;
617 mk_pointer message
, *page
= 0;
620 switch (http_status
) {
621 case M_CLIENT_BAD_REQUEST
:
622 page
= mk_request_set_default_page("Bad Request",
624 sr
->host_conf
->host_signature
);
627 case M_CLIENT_FORBIDDEN
:
628 page
= mk_request_set_default_page("Forbidden",
630 sr
->host_conf
->host_signature
);
633 case M_CLIENT_NOT_FOUND
:
634 mk_string_build(&message
.data
, &message
.len
,
635 "The requested URL was not found on this server.");
636 page
= mk_request_set_default_page("Not Found",
638 sr
->host_conf
->host_signature
);
639 mk_pointer_free(&message
);
642 case M_CLIENT_REQUEST_ENTITY_TOO_LARGE
:
643 mk_string_build(&message
.data
, &message
.len
,
644 "The request entity is too large.");
645 page
= mk_request_set_default_page("Entity too large",
647 sr
->host_conf
->host_signature
);
648 mk_pointer_free(&message
);
651 case M_CLIENT_METHOD_NOT_ALLOWED
:
652 page
= mk_request_set_default_page("Method Not Allowed",
654 sr
->host_conf
->host_signature
);
657 case M_CLIENT_REQUEST_TIMEOUT
:
658 case M_CLIENT_LENGTH_REQUIRED
:
661 case M_SERVER_NOT_IMPLEMENTED
:
662 page
= mk_request_set_default_page("Method Not Implemented",
664 sr
->host_conf
->host_signature
);
667 case M_SERVER_INTERNAL_ERROR
:
668 mk_string_build(&message
.data
, &message
.len
,
669 "Problems found running %s ", sr
->uri
);
670 page
= mk_request_set_default_page("Internal Server Error",
672 sr
->host_conf
->host_signature
);
673 mk_pointer_free(&message
);
676 case M_SERVER_HTTP_VERSION_UNSUP
:
677 mk_pointer_reset(&message
);
678 page
= mk_request_set_default_page("HTTP Version Not Supported",
680 sr
->host_conf
->host_signature
);
684 mk_header_set_http_status(sr
, http_status
);
686 sr
->headers
->content_length
= page
->len
;
689 sr
->headers
->location
= NULL
;
690 sr
->headers
->cgi
= SH_NOCGI
;
691 sr
->headers
->pconnections_left
= 0;
692 sr
->headers
->last_modified
= -1;
695 mk_mem_free(aux_message
);
698 mk_pointer_reset(&sr
->headers
->content_type
);
701 mk_pointer_set(&sr
->headers
->content_type
, "text/html\r\n");
704 mk_header_send(cr
->socket
, cr
, sr
);
706 if (page
&& sr
->method
!= HTTP_METHOD_HEAD
) {
707 n
= mk_socket_send(cr
->socket
, page
->data
, page
->len
);
708 mk_pointer_free(page
);
712 /* Turn off TCP_CORK */
713 mk_socket_set_cork_flag(cr
->socket
, TCP_CORK_OFF
);
716 /* Build error page */
717 mk_pointer
*mk_request_set_default_page(char *title
, mk_pointer message
,
723 p
= mk_mem_malloc(sizeof(mk_pointer
));
725 temp
= mk_pointer_to_buf(message
);
726 mk_string_build(&p
->data
, &p
->len
,
727 MK_REQUEST_DEFAULT_PAGE
, title
, temp
, signature
);
733 /* Create a memory allocation in order to handle the request data */
734 struct request
*mk_request_alloc()
736 struct request
*request
= 0;
738 request
= mk_mem_malloc(sizeof(struct request
));
739 request
->status
= VAR_OFF
; /* Request not processed yet */
740 request
->close_now
= VAR_OFF
;
742 mk_pointer_reset(&request
->body
);
743 request
->status
= VAR_ON
;
744 request
->method
= METHOD_NOT_FOUND
;
746 mk_pointer_reset(&request
->uri
);
747 request
->uri_processed
= NULL
;
748 request
->uri_twin
= VAR_OFF
;
750 request
->accept
.data
= NULL
;
751 request
->accept_language
.data
= NULL
;
752 request
->accept_encoding
.data
= NULL
;
753 request
->accept_charset
.data
= NULL
;
754 request
->content_length
= 0;
755 request
->content_type
.data
= NULL
;
756 request
->connection
.data
= NULL
;
757 request
->cookies
.data
= NULL
;
758 request
->host
.data
= NULL
;
759 request
->if_modified_since
.data
= NULL
;
760 request
->last_modified_since
.data
= NULL
;
761 request
->range
.data
= NULL
;
762 request
->referer
.data
= NULL
;
763 request
->resume
.data
= NULL
;
764 request
->user_agent
.data
= NULL
;
766 request
->post_variables
.data
= NULL
;
768 request
->user_uri
= NULL
;
769 mk_pointer_reset(&request
->query_string
);
771 request
->file_info
= NULL
;
772 request
->virtual_user
= NULL
;
773 request
->script_filename
= NULL
;
774 mk_pointer_reset(&request
->real_path
);
775 request
->host_conf
= config
->hosts
;
778 request
->bytes_to_send
= -1;
779 request
->bytes_offset
= 0;
780 request
->fd_file
= -1;
782 /* Response Headers */
783 request
->headers
= mk_header_create();
786 request
->handled_by
= NULL
;
791 void mk_request_free_list(struct client_request
*cr
)
793 struct request
*sr
= 0, *before
= 0;
797 MK_TRACE("Free struct client_request [FD %i]", cr
->socket
);
800 while (cr
->request
) {
801 sr
= before
= cr
->request
;
807 if (sr
!= cr
->request
) {
808 while (before
->next
!= sr
) {
809 before
= before
->next
;
821 void mk_request_free(struct request
*sr
)
823 if (sr
->fd_file
> 0) {
827 mk_mem_free(sr
->headers
->location
);
828 mk_mem_free(sr
->headers
);
831 mk_pointer_reset(&sr
->body
);
832 mk_pointer_reset(&sr
->uri
);
834 if (sr
->uri_twin
== VAR_ON
) {
835 mk_mem_free(sr
->uri_processed
);
838 mk_pointer_free(&sr
->post_variables
);
839 mk_mem_free(sr
->user_uri
);
840 mk_pointer_reset(&sr
->query_string
);
842 mk_mem_free(sr
->file_info
);
843 mk_mem_free(sr
->virtual_user
);
844 mk_mem_free(sr
->script_filename
);
845 mk_pointer_free(&sr
->real_path
);
849 /* Create a client request struct and put it on the
852 struct client_request
*mk_request_client_create(int socket
)
854 struct request_idx
*request_index
;
855 struct client_request
*cr
;
856 struct sched_connection
*sc
;
858 sc
= mk_sched_get_connection(NULL
, socket
);
859 cr
= mk_mem_malloc(sizeof(struct client_request
));
863 MK_TRACE("FAILED SOCKET: %i", socket
);
869 cr
->ipv4
= &sc
->ipv4
;
871 cr
->pipelined
= FALSE
;
872 cr
->counter_connections
= 0;
874 cr
->status
= MK_REQUEST_STATUS_INCOMPLETE
;
877 /* creation time in unix time */
878 cr
->init_time
= sc
->arrive_time
;
881 cr
->body
= mk_mem_malloc(MK_REQUEST_CHUNK
);
883 /* Buffer size based in Chunk bytes */
884 cr
->body_size
= MK_REQUEST_CHUNK
;
885 /* Current data length */
888 cr
->body_pos_end
= -1;
889 cr
->first_method
= HTTP_METHOD_UNKNOWN
;
891 /* Add this request to the thread request list */
892 request_index
= mk_sched_get_request_index();
893 if (!request_index
->first
) {
894 request_index
->first
= request_index
->last
= cr
;
897 request_index
->last
->next
= cr
;
898 request_index
->last
= cr
;
901 /* Set again the global list */
902 mk_sched_set_request_index(request_index
);
907 struct client_request
*mk_request_client_get(int socket
)
909 struct request_idx
*request_index
;
910 struct client_request
*cr
= NULL
;
912 request_index
= mk_sched_get_request_index();
913 cr
= request_index
->first
;
915 if (cr
->socket
== socket
) {
925 * From thread sched_list_node "list", remove the client_request
928 void mk_request_client_remove(int socket
)
930 struct request_idx
*request_index
;
931 struct client_request
*cr
, *aux
;
933 request_index
= mk_sched_get_request_index();
934 cr
= request_index
->first
;
937 if (cr
->socket
== socket
) {
938 if (cr
== request_index
->first
) {
939 request_index
->first
= cr
->next
;
942 aux
= request_index
->first
;
943 while (aux
->next
!= cr
) {
946 aux
->next
= cr
->next
;
948 request_index
->last
= aux
;
956 mk_mem_free(cr
->body
);
959 /* Update thread index */
960 mk_sched_set_request_index(request_index
);
963 struct header_toc
*mk_request_header_toc_create(int len
)
966 struct header_toc
*p
;
968 p
= (struct header_toc
*) mk_cache_get(mk_cache_header_toc
);
970 for (i
= 0; i
< len
; i
++) {
978 void mk_request_header_toc_parse(struct header_toc
*toc
, int toc_len
, char *data
, int len
)
984 for (i
= 0; i
< toc_len
&& p
&& l
< data
+ len
; i
++) {
985 l
= strstr(p
, MK_CRLF
);
997 void mk_request_ka_next(struct client_request
*cr
)
999 bzero(cr
->body
, sizeof(cr
->body
));
1000 cr
->first_method
= -1;
1001 cr
->body_pos_end
= -1;
1002 cr
->body_length
= 0;
1003 cr
->counter_connections
++;
1005 /* Update data for scheduler */
1006 cr
->init_time
= log_current_utime
;
1007 cr
->status
= MK_REQUEST_STATUS_INCOMPLETE
;