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 // mk_pointer_reset(&method);
95 pos
= mk_string_search(body
, " ");
96 if(pos
<=2 || pos
>=max_len_method
){
97 return METHOD_NOT_FOUND
;
101 method
.len
= (unsigned long) pos
;
103 int_method
= mk_http_method_check(method
);
108 int mk_http_protocol_check(char *protocol
)
110 if(strcasecmp(protocol
, HTTP_PROTOCOL_11_STR
)==0)
112 return HTTP_PROTOCOL_11
;
114 if(strcasecmp(protocol
, HTTP_PROTOCOL_10_STR
)==0)
116 return HTTP_PROTOCOL_10
;
118 if(strcasecmp(protocol
, HTTP_PROTOCOL_09_STR
)==0)
120 return HTTP_PROTOCOL_09
;
123 return HTTP_PROTOCOL_UNKNOWN
;
126 mk_pointer
mk_http_protocol_check_str(int protocol
)
128 if(protocol
==HTTP_PROTOCOL_11
)
130 return mk_http_protocol_11_p
;
132 if(protocol
==HTTP_PROTOCOL_10
)
134 return mk_http_protocol_10_p
;
136 if(protocol
==HTTP_PROTOCOL_09
)
138 return mk_http_protocol_09_p
;
141 return mk_http_protocol_null_p
;
144 int mk_http_init(struct client_request
*cr
, struct request
*sr
)
146 int debug_error
=0, bytes
=0;
147 char *location
=0, *real_location
=0; /* ruta para redireccion */
148 struct mimetype
*mime
;
149 mk_pointer gmt_file_unix_time
; // gmt time of server file (unix time)
153 /* Normal request default site */
154 if((strcmp(sr
->uri_processed
,"/"))==0)
156 sr
->real_path
.data
= strdup(sr
->host_conf
->documentroot
.data
);
157 sr
->real_path
.len
= sr
->host_conf
->documentroot
.len
;
160 if(sr
->user_home
==VAR_OFF
)
162 mk_buffer_cat(&sr
->real_path
, sr
->host_conf
->documentroot
.data
,
166 if(sr
->method
!=HTTP_METHOD_HEAD
){
170 sr
->file_info
= mk_file_get_info(sr
->real_path
.data
);
173 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
,
174 debug_error
, sr
->log
);
178 /* Check symbolic link file */
179 if(sr
->file_info
->is_link
== MK_FILE_TRUE
){
180 if(config
->symlink
==VAR_OFF
){
181 sr
->log
->final_response
=M_CLIENT_FORBIDDEN
;
182 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
,
183 debug_error
, sr
->log
);
188 char linked_file
[MAX_PATH
];
189 n
= readlink(sr
->real_path
.data
, linked_file
, MAX_PATH
);
191 if(Deny_Check(linked_file)==-1) {
192 sr->log->final_response=M_CLIENT_FORBIDDEN;
193 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, debug_error, sr->log);
201 /* Plugin Stage 40 */
202 mk_plugin_stage_run(MK_PLUGIN_STAGE_40
, cr
, sr
);
204 /* is it a valid directory ? */
205 if(sr
->file_info
->is_directory
== MK_FILE_TRUE
) {
206 /* This pointer never must be freed */
207 mk_pointer index_file
;
210 We have to check if exist an slash to the end of
211 this string, if doesn't exist we send a redirection header
213 if(sr
->uri_processed
[strlen(sr
->uri_processed
) - 1] != '/') {
215 host
= mk_pointer_to_buf(sr
->host
);
217 m_build_buffer(&location
, &len
, "%s/", sr
->uri_processed
);
218 if(config
->serverport
== config
->standard_port
)
220 m_build_buffer(&real_location
, &len
, "http://%s%s",
225 m_build_buffer(&real_location
, &len
, "http://%s:%i%s",
226 host
, config
->serverport
,
231 sr
->headers
->status
= M_REDIR_MOVED
;
232 sr
->headers
->content_length
= -1;
233 mk_pointer_reset(&sr
->headers
->content_type
);
234 sr
->headers
->location
= real_location
;
235 sr
->headers
->cgi
= SH_NOCGI
;
236 sr
->headers
->pconnections_left
=
237 (config
->max_keep_alive_request
-
238 cr
->counter_connections
);
240 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
241 mk_socket_set_cork_flag(cr
->socket
, TCP_CORK_OFF
);
244 * we do not free() real_location
245 * as it's freed by iov
247 mk_mem_free(location
);
248 sr
->headers
->location
=NULL
;
252 /* looking for a index file */
253 index_file
= mk_request_index(sr
->real_path
.data
);
255 if(!index_file
.data
) {
256 /* No index file found, show the content directory */
257 if(sr
->host_conf
->getdir
==VAR_ON
) {
260 //getdir_res = mk_dirhtml_init(cr, sr);
262 if(getdir_res
== -1){
263 mk_request_error(M_CLIENT_FORBIDDEN
, cr
,
270 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
,
276 mk_mem_free(sr
->file_info
);
277 mk_pointer_free(&sr
->real_path
);
279 sr
->real_path
= index_file
;
280 sr
->file_info
= mk_file_get_info(sr
->real_path
.data
);
284 /* read permission */
285 if(sr
->file_info
->read_access
== MK_FILE_FALSE
){
286 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
290 /* Matching MimeType */
291 mime
= mk_mimetype_find(&sr
->real_path
);
294 mime
= mimetype_default
;
298 palm
= mk_palm_check_request(cr
, sr
);
301 mk_palm_send_response(cr
, sr
, palm
);
307 if(sr
->file_info
->size
< 0) {
308 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
, 1, sr
->log
);
312 /* counter connections */
313 sr
->headers
->pconnections_left
= (int)
314 (config
->max_keep_alive_request
- cr
->counter_connections
);
318 PutDate_string((time_t) sr
->file_info
->last_modification
);
320 if(sr
->if_modified_since
.data
&& sr
->method
==HTTP_METHOD_GET
){
321 time_t date_client
; // Date send by client
322 time_t date_file_server
; // Date server file
324 date_client
= PutDate_unix(sr
->if_modified_since
.data
);
325 date_file_server
= sr
->file_info
->last_modification
;
327 if( (date_file_server
<= date_client
) && (date_client
> 0) )
329 sr
->headers
->status
= M_NOT_MODIFIED
;
330 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
331 mk_pointer_free(&gmt_file_unix_time
);
335 sr
->headers
->status
= M_HTTP_OK
;
336 sr
->headers
->cgi
= SH_NOCGI
;
337 sr
->headers
->last_modified
= gmt_file_unix_time
;
338 sr
->headers
->location
= NULL
;
340 /* Object size for log and response headers */
341 sr
->log
->size
= sr
->headers
->content_length
= sr
->file_info
->size
;
342 sr
->log
->size_p
= sr
->headers
->content_length_p
=
343 mk_utils_int2mkp(sr
->file_info
->size
);
345 if(sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
347 sr
->headers
->content_type
= mime
->type
;
349 if(sr
->range
.data
!=NULL
&& config
->resume
==VAR_ON
){
350 if(mk_http_range_parse(sr
)<0)
352 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
,
354 mk_pointer_free(&gmt_file_unix_time
);
357 if(sr
->headers
->ranges
[0]>=0 || sr
->headers
->ranges
[1]>=0)
358 sr
->headers
->status
= M_HTTP_PARTIAL
;
361 else{ /* without content-type */
362 mk_pointer_reset(&sr
->headers
->content_type
);
365 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
367 if(sr
->headers
->content_length
==0){
372 if((sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
373 && sr
->file_info
->size
>0)
375 sr
->fd_file
= open(sr
->real_path
.data
, config
->open_flags
);
377 if(sr
->fd_file
== -1){
382 /* Calc bytes to send & offset */
383 if(mk_http_range_set(sr
, sr
->file_info
->size
)!=0)
385 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
,
390 bytes
= SendFile(cr
->socket
, sr
);
397 * Check if a connection can continue open using as criteria
398 * the keepalive headers vars and Monkey configuration
400 int mk_http_keepalive_check(int socket
, struct client_request
*cr
)
407 if(config
->keep_alive
==VAR_OFF
|| cr
->request
->keep_alive
==VAR_OFF
)
412 if(cr
->counter_connections
>=config
->max_keep_alive_request
)
420 int mk_http_range_set(struct request
*sr
, long file_size
)
422 struct header_values
*sh
= sr
->headers
;
424 sr
->bytes_to_send
= file_size
;
425 sr
->bytes_offset
= 0;
427 if(config
->resume
==VAR_ON
&& sr
->range
.data
){
429 if(sh
->ranges
[0]>=0 && sh
->ranges
[1]==-1){
430 sr
->bytes_offset
= sh
->ranges
[0];
431 sr
->bytes_to_send
= file_size
- sr
->bytes_offset
;
435 if(sh
->ranges
[0]>=0 && sh
->ranges
[1]>=0){
436 sr
->bytes_offset
= sh
->ranges
[0];
437 sr
->bytes_to_send
= labs(sh
->ranges
[1]-sh
->ranges
[0])+1;
441 if(sh
->ranges
[0]==-1 && sh
->ranges
[1]>0){
442 sr
->bytes_to_send
= sh
->ranges
[1];
443 sr
->bytes_offset
= file_size
- sh
->ranges
[1];
446 if(sr
->bytes_offset
>file_size
|| sr
->bytes_to_send
>file_size
)
451 lseek(sr
->fd_file
, sr
->bytes_offset
, SEEK_SET
);
458 int mk_http_range_parse(struct request
*sr
)
460 int eq_pos
, sep_pos
, len
;
466 if((eq_pos
= mk_string_search_n(sr
->range
.data
, "=",
470 if(strncasecmp(sr
->range
.data
, "Bytes", eq_pos
)!=0)
473 if((sep_pos
= mk_string_search_n(sr
->range
.data
, "-",
480 if(eq_pos
+1 == sep_pos
){
481 sr
->headers
->ranges
[0] = -1;
482 sr
->headers
->ranges
[1] = (unsigned long) atol(sr
->range
.data
+ sep_pos
+ 1);
484 if(sr
->headers
->ranges
[1]<=0)
492 if( (eq_pos
+1 != sep_pos
) && (len
> sep_pos
+ 1))
494 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+1, sep_pos
);
495 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);
498 buffer
= mk_string_copy_substr(sr
->range
.data
, sep_pos
+1, len
);
499 sr
->headers
->ranges
[1] = (unsigned long) atol(buffer
);
502 if(sr
->headers
->ranges
[1]<=0 ||
503 sr
->headers
->ranges
[0]>sr
->headers
->ranges
[1])
511 if( (eq_pos
+1 != sep_pos
) && (len
== sep_pos
+ 1))
513 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+1, len
);
514 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);
523 * Check if client request still has pending data
525 * Return 0 when all expected data has arrived or -1 when
526 * the connection is on a pending status due to HTTP spec
528 * This function is called from request.c :: mk_handler_read(..)
530 int mk_http_pending_request(struct client_request
*cr
)
535 n
= mk_string_search(cr
->body
, mk_endblock
.data
);
542 if(cr
->first_block_end
<0)
544 cr
->first_block_end
= n
;
547 str
= cr
->body
+ n
+ mk_endblock
.len
;
549 if(cr
->first_method
== HTTP_METHOD_UNKNOWN
){
550 cr
->first_method
= mk_http_method_get(cr
->body
);
553 if(cr
->first_method
== HTTP_METHOD_POST
)
555 if(cr
->first_block_end
> 0){
556 /* if first block has ended, we need to verify if exists
557 * a previous block end, that will means that the POST
558 * method has sent the whole information.
559 * just for ref: pipelining is not allowed with POST
561 if(cr
->first_block_end
== cr
->body_length
-mk_endblock
.len
){
562 /* Content-length is required, if is it not found,
563 * we pass as successfull in order to raise the error
566 if(mk_method_post_content_length(cr
->body
) < 0){
567 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
572 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
581 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
585 mk_pointer
*mk_http_status_get(short int code
)
589 l
= mk_http_status_list
;
604 void mk_http_status_add(short int val
[2])
608 mk_list_sint_t
*list
, *new;
610 for(i
=val
[0];i
<=val
[1]; i
++)
613 new = mk_mem_malloc(sizeof(mk_list_sint_t
));
617 str_val
= mk_mem_malloc(6);
618 snprintf(str_val
, len
-1, "%i", i
);
620 new->value
.data
= str_val
;
623 if(!mk_http_status_list
)
625 mk_http_status_list
= new;
628 list
= mk_http_status_list
;
638 void mk_http_status_list_init()
641 short int success
[2] = {200, 206};
642 short int redirections
[2] = {300, 305};
643 short int client_errors
[2] = {400, 415};
644 short int server_errors
[2] = {500, 505};
646 mk_http_status_add(success
);
647 mk_http_status_add(redirections
);
648 mk_http_status_add(client_errors
);
649 mk_http_status_add(server_errors
);