1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <sys/types.h>
33 #include "http_status.h"
46 int mk_http_method_check(mk_pointer method
)
48 if(strncasecmp(method
.data
,
52 return HTTP_METHOD_GET
;
55 if(strncasecmp(method
.data
,
59 return HTTP_METHOD_POST
;
62 if(strncasecmp(method
.data
,
66 return HTTP_METHOD_HEAD
;
69 return METHOD_NOT_FOUND
;
72 mk_pointer
mk_http_method_check_str(int method
)
76 return mk_http_method_get_p
;
78 case HTTP_METHOD_POST
:
79 return mk_http_method_post_p
;
81 case HTTP_METHOD_HEAD
:
82 return mk_http_method_head_p
;
84 return mk_http_method_null_p
;
87 int mk_http_method_get(char *body
)
89 int int_method
, pos
= 0;
90 int max_len_method
= 5;
93 /* Max method length is 4 (POST/HEAD) */
94 pos
= mk_string_char_search(body
, ' ', 4);
95 if(pos
<=2 || pos
>=max_len_method
){
96 return METHOD_NOT_FOUND
;
100 method
.len
= (unsigned long) pos
;
102 int_method
= mk_http_method_check(method
);
107 int mk_http_protocol_check(char *protocol
, int len
)
109 if(strncasecmp(protocol
, HTTP_PROTOCOL_11_STR
, len
)==0)
111 return HTTP_PROTOCOL_11
;
113 if(strncasecmp(protocol
, HTTP_PROTOCOL_10_STR
, len
)==0)
115 return HTTP_PROTOCOL_10
;
117 if(strncasecmp(protocol
, HTTP_PROTOCOL_09_STR
, len
)==0)
119 return HTTP_PROTOCOL_09
;
122 return HTTP_PROTOCOL_UNKNOWN
;
125 mk_pointer
mk_http_protocol_check_str(int protocol
)
127 if(protocol
==HTTP_PROTOCOL_11
)
129 return mk_http_protocol_11_p
;
131 if(protocol
==HTTP_PROTOCOL_10
)
133 return mk_http_protocol_10_p
;
135 if(protocol
==HTTP_PROTOCOL_09
)
137 return mk_http_protocol_09_p
;
140 return mk_http_protocol_null_p
;
143 int mk_http_init(struct client_request
*cr
, struct request
*sr
)
145 int debug_error
=0, bytes
=0;
146 struct mimetype
*mime
;
147 mk_pointer gmt_file_unix_time
; // gmt time of server file (unix time)
150 /* Normal request default site */
151 if((strcmp(sr
->uri_processed
,"/"))==0)
153 sr
->real_path
.data
= mk_string_dup(sr
->host_conf
->documentroot
.data
);
154 sr
->real_path
.len
= sr
->host_conf
->documentroot
.len
;
157 if(sr
->user_home
==VAR_OFF
)
159 mk_buffer_cat(&sr
->real_path
, sr
->host_conf
->documentroot
.data
,
163 if(sr
->method
!=HTTP_METHOD_HEAD
){
167 sr
->file_info
= mk_file_get_info(sr
->real_path
.data
);
170 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
,
171 debug_error
, sr
->log
);
175 /* Check symbolic link file */
176 if(sr
->file_info
->is_link
== MK_FILE_TRUE
){
177 if(config
->symlink
==VAR_OFF
){
178 sr
->log
->final_response
=M_CLIENT_FORBIDDEN
;
179 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
,
180 debug_error
, sr
->log
);
185 char linked_file
[MAX_PATH
];
186 n
= readlink(sr
->real_path
.data
, linked_file
, MAX_PATH
);
188 if(Deny_Check(linked_file)==-1) {
189 sr->log->final_response=M_CLIENT_FORBIDDEN;
190 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, debug_error, sr->log);
198 /* is it a valid directory ? */
199 if(sr
->file_info
->is_directory
== MK_FILE_TRUE
) {
200 /* Send redirect header if end slash is not found */
201 if(mk_http_directory_redirect_check(cr
, sr
) == -1){
202 /* Redirect has been sent */
206 /* looking for a index file */
207 mk_pointer index_file
;
208 index_file
= mk_request_index(sr
->real_path
.data
);
210 if(index_file
.data
) {
211 mk_mem_free(sr
->file_info
);
212 mk_pointer_free(&sr
->real_path
);
214 sr
->real_path
= index_file
;
215 sr
->file_info
= mk_file_get_info(sr
->real_path
.data
);
219 /* read permissions and check file */
220 if(sr
->file_info
->read_access
== MK_FILE_FALSE
){
221 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
225 /* Matching MimeType */
226 mime
= mk_mimetype_find(&sr
->real_path
);
229 mime
= mimetype_default
;
232 /* Plugin Stage 40: look for handlers for this request */
233 if(mk_plugin_stage_run(MK_PLUGIN_STAGE_40
, cr
, sr
) == 0){
237 if(sr
->file_info
->is_directory
== MK_FILE_TRUE
){
238 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
242 /* FIXME: Move palm code to a plugin */
243 palm
= mk_palm_check_request(cr
, sr
);
246 mk_palm_send_response(cr
, sr
, palm
);
252 if(sr
->file_info
->size
< 0) {
253 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
, 1, sr
->log
);
257 /* counter connections */
258 sr
->headers
->pconnections_left
= (int)
259 (config
->max_keep_alive_request
- cr
->counter_connections
);
263 PutDate_string((time_t) sr
->file_info
->last_modification
);
265 if(sr
->if_modified_since
.data
&& sr
->method
==HTTP_METHOD_GET
){
266 time_t date_client
; // Date send by client
267 time_t date_file_server
; // Date server file
269 date_client
= PutDate_unix(sr
->if_modified_since
.data
);
270 date_file_server
= sr
->file_info
->last_modification
;
272 if( (date_file_server
<= date_client
) && (date_client
> 0) )
274 sr
->headers
->status
= M_NOT_MODIFIED
;
275 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
276 mk_pointer_free(&gmt_file_unix_time
);
280 sr
->headers
->status
= M_HTTP_OK
;
281 sr
->headers
->cgi
= SH_NOCGI
;
282 sr
->headers
->last_modified
= gmt_file_unix_time
;
283 sr
->headers
->location
= NULL
;
285 /* Object size for log and response headers */
286 sr
->log
->size
= sr
->headers
->content_length
= sr
->file_info
->size
;
287 sr
->log
->size_p
= sr
->headers
->content_length_p
=
288 mk_utils_int2mkp(sr
->file_info
->size
);
290 if(sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
292 sr
->headers
->content_type
= mime
->type
;
294 if(sr
->range
.data
!=NULL
&& config
->resume
==VAR_ON
){
295 if(mk_http_range_parse(sr
)<0)
297 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
,
299 mk_pointer_free(&gmt_file_unix_time
);
302 if(sr
->headers
->ranges
[0]>=0 || sr
->headers
->ranges
[1]>=0)
303 sr
->headers
->status
= M_HTTP_PARTIAL
;
306 else{ /* without content-type */
307 mk_pointer_reset(&sr
->headers
->content_type
);
310 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
312 if(sr
->headers
->content_length
==0){
317 if((sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
318 && sr
->file_info
->size
>0)
320 sr
->fd_file
= open(sr
->real_path
.data
, config
->open_flags
);
322 if(sr
->fd_file
== -1){
327 /* Calc bytes to send & offset */
328 if(mk_http_range_set(sr
, sr
->file_info
->size
)!=0)
330 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
,
335 bytes
= SendFile(cr
->socket
, sr
);
341 int mk_http_directory_redirect_check(struct client_request
*cr
,
346 char *real_location
=0;
350 * We have to check if exist an slash to the end of
351 * this string, if doesn't exist we send a redirection header
353 if(sr
->uri_processed
[strlen(sr
->uri_processed
) - 1] == '/') {
357 host
= mk_pointer_to_buf(sr
->host
);
359 m_build_buffer(&location
, &len
, "%s/", sr
->uri_processed
);
360 if(config
->serverport
== config
->standard_port
)
362 m_build_buffer(&real_location
, &len
, "http://%s%s",
366 m_build_buffer(&real_location
, &len
, "http://%s:%i%s",
367 host
, config
->serverport
,
373 sr
->headers
->status
= M_REDIR_MOVED
;
374 sr
->headers
->content_length
= -1;
375 mk_pointer_reset(&sr
->headers
->content_type
);
376 sr
->headers
->location
= real_location
;
377 sr
->headers
->cgi
= SH_NOCGI
;
378 sr
->headers
->pconnections_left
=
379 (config
->max_keep_alive_request
-
380 cr
->counter_connections
);
382 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
383 mk_socket_set_cork_flag(cr
->socket
, TCP_CORK_OFF
);
386 * we do not free() real_location
387 * as it's freed by iov
389 mk_mem_free(location
);
390 sr
->headers
->location
=NULL
;
395 * Check if a connection can continue open using as criteria
396 * the keepalive headers vars and Monkey configuration
398 int mk_http_keepalive_check(int socket
, struct client_request
*cr
)
405 if(config
->keep_alive
==VAR_OFF
|| cr
->request
->keep_alive
==VAR_OFF
)
410 if(cr
->counter_connections
>=config
->max_keep_alive_request
)
418 int mk_http_range_set(struct request
*sr
, long file_size
)
420 struct header_values
*sh
= sr
->headers
;
422 sr
->bytes_to_send
= file_size
;
423 sr
->bytes_offset
= 0;
425 if(config
->resume
==VAR_ON
&& sr
->range
.data
){
427 if(sh
->ranges
[0]>=0 && sh
->ranges
[1]==-1){
428 sr
->bytes_offset
= sh
->ranges
[0];
429 sr
->bytes_to_send
= file_size
- sr
->bytes_offset
;
433 if(sh
->ranges
[0]>=0 && sh
->ranges
[1]>=0){
434 sr
->bytes_offset
= sh
->ranges
[0];
435 sr
->bytes_to_send
= labs(sh
->ranges
[1]-sh
->ranges
[0])+1;
439 if(sh
->ranges
[0]==-1 && sh
->ranges
[1]>0){
440 sr
->bytes_to_send
= sh
->ranges
[1];
441 sr
->bytes_offset
= file_size
- sh
->ranges
[1];
444 if(sr
->bytes_offset
>file_size
|| sr
->bytes_to_send
>file_size
)
449 lseek(sr
->fd_file
, sr
->bytes_offset
, SEEK_SET
);
456 int mk_http_range_parse(struct request
*sr
)
458 int eq_pos
, sep_pos
, len
;
464 if((eq_pos
= mk_string_search_n(sr
->range
.data
, "=",
468 if(strncasecmp(sr
->range
.data
, "Bytes", eq_pos
)!=0)
471 if((sep_pos
= mk_string_search_n(sr
->range
.data
, "-",
478 if(eq_pos
+1 == sep_pos
){
479 sr
->headers
->ranges
[0] = -1;
480 sr
->headers
->ranges
[1] = (unsigned long) atol(sr
->range
.data
+ sep_pos
+ 1);
482 if(sr
->headers
->ranges
[1]<=0)
490 if( (eq_pos
+1 != sep_pos
) && (len
> sep_pos
+ 1))
492 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+1, sep_pos
);
493 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);
496 buffer
= mk_string_copy_substr(sr
->range
.data
, sep_pos
+1, len
);
497 sr
->headers
->ranges
[1] = (unsigned long) atol(buffer
);
500 if(sr
->headers
->ranges
[1]<=0 ||
501 sr
->headers
->ranges
[0]>sr
->headers
->ranges
[1])
509 if( (eq_pos
+1 != sep_pos
) && (len
== sep_pos
+ 1))
511 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+1, len
);
512 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);
521 * Check if client request still has pending data
523 * Return 0 when all expected data has arrived or -1 when
524 * the connection is on a pending status due to HTTP spec
526 * This function is called from request.c :: mk_handler_read(..)
528 int mk_http_pending_request(struct client_request
*cr
)
533 len
= cr
->body_length
;
535 /* try to match CRLF end */
536 if(strcmp(cr
->body
+len
-mk_endblock
.len
, mk_endblock
.data
) == 0){
537 n
= len
-mk_endblock
.len
;
540 n
= mk_string_search(cr
->body
, mk_endblock
.data
);
548 if(cr
->first_block_end
<0)
550 cr
->first_block_end
= n
;
553 str
= cr
->body
+ n
+ mk_endblock
.len
;
555 if(cr
->first_method
== HTTP_METHOD_UNKNOWN
){
556 cr
->first_method
= mk_http_method_get(cr
->body
);
559 if(cr
->first_method
== HTTP_METHOD_POST
)
561 if(cr
->first_block_end
> 0){
562 /* if first block has ended, we need to verify if exists
563 * a previous block end, that will means that the POST
564 * method has sent the whole information.
565 * just for ref: pipelining is not allowed with POST
567 if(cr
->first_block_end
== cr
->body_length
-mk_endblock
.len
){
568 /* Content-length is required, if is it not found,
569 * we pass as successfull in order to raise the error
572 if(mk_method_post_content_length(cr
->body
) < 0){
573 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
578 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
587 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
591 mk_pointer
*mk_http_status_get(short int code
)
595 l
= mk_http_status_list
;
610 void mk_http_status_add(short int val
[2])
614 mk_list_sint_t
*list
, *new;
616 for(i
=val
[0];i
<=val
[1]; i
++)
619 new = mk_mem_malloc(sizeof(mk_list_sint_t
));
623 str_val
= mk_mem_malloc(6);
624 snprintf(str_val
, len
-1, "%i", i
);
626 new->value
.data
= str_val
;
629 if(!mk_http_status_list
)
631 mk_http_status_list
= new;
634 list
= mk_http_status_list
;
644 void mk_http_status_list_init()
647 short int success
[2] = {200, 206};
648 short int redirections
[2] = {300, 305};
649 short int client_errors
[2] = {400, 415};
650 short int server_errors
[2] = {500, 505};
652 mk_http_status_add(success
);
653 mk_http_status_add(redirections
);
654 mk_http_status_add(client_errors
);
655 mk_http_status_add(server_errors
);