3 * Copyright (C) 2001-2008, Eduardo Silva P.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include <sys/types.h>
31 #include "http_status.h"
44 #define O_NOATIME 01000000
46 int mk_http_method_check(char *method
)
48 if(strcmp(method
, HTTP_METHOD_GET_STR
)==0)
50 return HTTP_METHOD_GET
;
53 if(strcmp(method
, HTTP_METHOD_POST_STR
)==0)
55 return HTTP_METHOD_POST
;
58 if(strcmp(method
, HTTP_METHOD_HEAD_STR
)==0)
60 return HTTP_METHOD_HEAD
;
63 return METHOD_NOT_FOUND
;
66 char *mk_http_method_check_str(int method
)
70 return (char *) HTTP_METHOD_GET_STR
;
72 case HTTP_METHOD_POST
:
73 return (char *) HTTP_METHOD_POST_STR
;
75 case HTTP_METHOD_HEAD
:
76 return (char *) HTTP_METHOD_HEAD_STR
;
82 int mk_http_method_get(mk_pointer body
)
84 int int_method
, pos
= 0, max_length_method
= 5;
87 pos
= mk_string_search(body
.data
, " ");
88 if(pos
<=2 || pos
>=max_length_method
){
92 str_method
= mk_mem_malloc(max_length_method
);
93 strncpy(str_method
, body
.data
, pos
);
96 int_method
= mk_http_method_check(str_method
);
97 mk_mem_free(str_method
);
102 int mk_http_protocol_check(char *protocol
)
104 if(strcmp(protocol
, HTTP_PROTOCOL_11_STR
)==0)
106 return HTTP_PROTOCOL_11
;
108 if(strcmp(protocol
, HTTP_PROTOCOL_10_STR
)==0)
110 return HTTP_PROTOCOL_10
;
112 if(strcmp(protocol
, HTTP_PROTOCOL_09_STR
)==0)
114 return HTTP_PROTOCOL_09
;
117 return HTTP_PROTOCOL_UNKNOWN
;
120 char *mk_http_protocol_check_str(int protocol
)
122 if(protocol
==HTTP_PROTOCOL_11
)
124 return (char *) HTTP_PROTOCOL_11_STR
;
126 if(protocol
==HTTP_PROTOCOL_10
)
128 return (char *) HTTP_PROTOCOL_10_STR
;
130 if(protocol
==HTTP_PROTOCOL_09
)
132 return (char *) HTTP_PROTOCOL_09_STR
;
138 int mk_http_init(struct client_request
*cr
, struct request
*sr
)
140 int debug_error
=0, bytes
=0;
141 char *location
=0, *real_location
=0; /* ruta para redireccion */
143 mk_pointer gmt_file_unix_time
; // gmt time of server file (unix time)
144 struct file_info
*path_info
;
147 /* Normal request default site */
148 if((strcmp(sr
->uri_processed
,"/"))==0)
150 m_build_buffer(&sr
->real_path
, &len
, "%s",
151 sr
->host_conf
->documentroot
);
154 if(sr
->user_home
==VAR_OFF
)
156 m_build_buffer(&sr
->real_path
, &len
, "%s%s",
157 sr
->host_conf
->documentroot
,
161 if(sr
->method
!=HTTP_METHOD_HEAD
){
165 path_info
= mk_file_get_info(sr
->real_path
);
167 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
,
168 debug_error
, sr
->log
);
172 /* Check symbolic link file */
173 if(path_info
->is_link
== MK_FILE_TRUE
){
174 if(config
->symlink
==VAR_OFF
){
175 sr
->log
->final_response
=M_CLIENT_FORBIDDEN
;
176 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
,
177 debug_error
, sr
->log
);
181 char linked_file
[MAX_PATH
];
182 readlink(sr
->real_path
, linked_file
, MAX_PATH
);
184 if(Deny_Check(linked_file)==-1) {
185 sr->log->final_response=M_CLIENT_FORBIDDEN;
186 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, debug_error, sr->log);
193 /* is it a valid directory ? */
194 if(path_info
->is_directory
== MK_FILE_TRUE
) {
195 /* This pointer never must be freed */
196 char *index_file
= 0;
199 We have to check if exist an slash to the end of
200 this string, if doesn't exist we send a redirection header
202 if(sr
->uri_processed
[strlen(sr
->uri_processed
) - 1] != '/') {
204 host
= mk_pointer_to_buf(sr
->host
);
206 m_build_buffer(&location
, &len
, "%s/", sr
->uri_processed
);
207 if(config
->serverport
== config
->standard_port
)
209 m_build_buffer(&real_location
, &len
, "http://%s%s",
214 m_build_buffer(&real_location
, &len
, "http://%s:%i%s",
215 host
, config
->serverport
,
220 sr
->headers
->status
= M_REDIR_MOVED
;
221 sr
->headers
->content_length
= 0;
222 sr
->headers
->content_type
= NULL
;
223 sr
->headers
->location
= real_location
;
224 sr
->headers
->cgi
= SH_NOCGI
;
225 sr
->headers
->pconnections_left
=
226 (config
->max_keep_alive_request
-
227 cr
->counter_connections
);
229 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
231 mk_mem_free(location
);
232 mk_mem_free(real_location
);
233 sr
->headers
->location
=NULL
;
237 /* looking for a index file */
238 index_file
= (char *) mk_request_index(sr
->real_path
);
241 /* No index file found, show the content directory */
242 if(sr
->host_conf
->getdir
==VAR_ON
) {
245 getdir_res
= mk_dirhtml_init(cr
, sr
);
247 if(getdir_res
== -1){
248 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
254 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
259 mk_mem_free(path_info
);
261 m_build_buffer_from_buffer(sr
->real_path
, "%s", index_file
);
263 path_info
= mk_file_get_info(sr
->real_path
);
267 /* read permission */
268 if(path_info
->read_access
== MK_FILE_FALSE
){
269 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
273 /* Matching MimeType */
274 mime_info
=Mimetype_Find(sr
->real_path
);
278 * Some returns are not freeing all memory blocks allocated
282 struct file_info
*fcgi
;
283 fcgi
= mk_file_get_info(mime_info
[1]);
285 /* executable script (e.g PHP) ? */
290 /* is it normal file ? */
291 if(fcgi
->is_directory
==MK_FILE_TRUE
|| fcgi
->exec_access
==MK_FILE_FALSE
){
292 mk_request_error(M_SERVER_INTERNAL_ERROR
, cr
, sr
, 1, sr
->log
);
293 Mimetype_free(mime_info
);
297 * FIXME: CHECK FOR TARGET PERMISSION AS GCI DOES
299 ftarget = mk_file_get_info(sr->script_filename);
305 sr
->log
->final_response
=M_HTTP_OK
;
306 sr
->script_filename
=mk_string_dup(sr
->real_path
);
308 arg_script
[0] = mime_info
[1];
309 arg_script
[1] = sr
->script_filename
;
310 arg_script
[2] = NULL
;
312 if(sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
314 cgi_status
=M_CGI_run(cr
, sr
, mime_info
[1], arg_script
);
318 case -2: /* Timeout */
319 sr
->log
->final_response
=M_CLIENT_REQUEST_TIMEOUT
;
321 case -3: /* Internal server Error */
322 sr
->log
->final_response
=M_SERVER_INTERNAL_ERROR
;
325 sr
->make_log
=VAR_OFF
;
328 sr
->log
->final_response
=M_HTTP_OK
;
332 if(cgi_status
==M_CGI_TIMEOUT
||
333 cgi_status
==M_CGI_INTERNAL_SERVER_ERR
)
335 mk_request_error(sr
->log
->final_response
, cr
,
339 Mimetype_free(mime_info
);
345 if(path_info
->size
< 0) {
346 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
, 1, sr
->log
);
347 Mimetype_free(mime_info
);
351 /* counter connections */
352 sr
->headers
->pconnections_left
= (int)
353 (config
->max_keep_alive_request
- cr
->counter_connections
);
356 PutDate_string((time_t) path_info
->last_modification
);
358 if(sr
->if_modified_since
.data
&& sr
->method
==HTTP_METHOD_GET
){
359 time_t date_client
; // Date send by client
360 time_t date_file_server
; // Date server file
363 date_client
= PutDate_unix(sr
->if_modified_since
.data
);
364 date_file_server
= PutDate_unix(gmt_file_unix_time
.data
);
366 if( (date_file_server
<= date_client
) && (date_client
> 0) )
368 sr
->headers
->status
= M_NOT_MODIFIED
;
369 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
370 Mimetype_free(mime_info
);
371 mk_mem_free(path_info
);
372 mk_pointer_free(gmt_file_unix_time
);
376 sr
->headers
->status
= M_HTTP_OK
;
377 sr
->headers
->content_length
= path_info
->size
;
378 sr
->headers
->cgi
= SH_NOCGI
;
379 sr
->headers
->last_modified
= gmt_file_unix_time
.data
;
380 sr
->headers
->location
= NULL
;
382 sr
->log
->size
= path_info
->size
;
383 if(sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
385 sr
->headers
->content_type
= mime_info
[0];
387 if(sr
->range
.data
!=NULL
&& config
->resume
==VAR_ON
){
388 if(mk_http_range_parse(sr
)<0)
390 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
,
392 mk_pointer_free(gmt_file_unix_time
);
395 if(sr
->headers
->ranges
[0]>=0 || sr
->headers
->ranges
[1]>=0)
396 sr
->headers
->status
= M_HTTP_PARTIAL
;
399 else{ /* without content-type */
400 sr
->headers
->content_type
= NULL
;
403 mk_header_send(cr
->socket
, cr
, sr
, sr
->log
);
405 if(sr
->headers
->content_length
==0){
406 Mimetype_free(mime_info
);
411 if((sr
->method
==HTTP_METHOD_GET
|| sr
->method
==HTTP_METHOD_POST
)
412 && path_info
->size
>0)
414 /* O_NOATIME will just work if process owner has
416 sr
->fd_file
= open(sr
->real_path
,
417 O_RDONLY
|O_NOATIME
|O_NONBLOCK
);
419 /* Calc bytes to send & offset */
420 if(mk_http_range_set(sr
, path_info
->size
)!=0)
422 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
,
427 mk_socket_set_cork_flag(cr
->socket
, TCP_CORK_OFF
);
428 bytes
= SendFile(cr
->socket
, sr
);
431 Mimetype_free(mime_info
);
432 mk_mem_free(path_info
);
433 sr
->headers
->content_type
=NULL
;
439 * Check if a connection can continue open using as criteria
440 * the keepalive headers vars and Monkey configuration
442 int mk_http_keepalive_check(int socket
, struct client_request
*cr
)
449 if(config
->keep_alive
==VAR_OFF
|| cr
->request
->keep_alive
==VAR_OFF
)
454 if(cr
->counter_connections
>=config
->max_keep_alive_request
)
462 int mk_http_range_set(struct request
*sr
, long file_size
)
464 struct header_values
*sh
= sr
->headers
;
466 sr
->bytes_to_send
= file_size
;
467 sr
->bytes_offset
= 0;
469 if(config
->resume
==VAR_ON
&& sr
->range
.data
){
471 if(sh
->ranges
[0]>=0 && sh
->ranges
[1]==-1){
472 sr
->bytes_offset
= sh
->ranges
[0];
473 sr
->bytes_to_send
= file_size
- sr
->bytes_offset
;
477 if(sh
->ranges
[0]>=0 && sh
->ranges
[1]>=0){
478 sr
->bytes_offset
= sh
->ranges
[0];
479 sr
->bytes_to_send
= labs(sh
->ranges
[1]-sh
->ranges
[0])+1;
483 if(sh
->ranges
[0]==-1 && sh
->ranges
[1]>=0){
484 sr
->bytes_to_send
= file_size
- sh
->ranges
[1];
487 if(sr
->bytes_offset
>file_size
|| sr
->bytes_to_send
>file_size
)
492 lseek(sr
->fd_file
, sr
->bytes_offset
, SEEK_SET
);
499 int mk_http_range_parse(struct request
*sr
)
501 int eq_pos
, sep_pos
, len
;
507 if((eq_pos
= mk_string_search_n(sr
->range
.data
, "=",
511 if(strncasecmp(sr
->range
.data
, "Bytes", eq_pos
)!=0)
514 if((sep_pos
= mk_string_search_n(sr
->range
.data
, "-",
521 if(eq_pos
+1 == sep_pos
){
522 sr
->headers
->ranges
[0] = -1;
523 sr
->headers
->ranges
[1] = (unsigned long) atol(sr
->range
.data
+ sep_pos
+ 1);
525 if(sr
->headers
->ranges
[1]<=0)
533 if( (eq_pos
+1 != sep_pos
) && (len
> sep_pos
+ 1))
535 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+1, sep_pos
);
536 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);
539 buffer
= mk_string_copy_substr(sr
->range
.data
, sep_pos
+1, len
);
540 sr
->headers
->ranges
[1] = (unsigned long) atol(buffer
);
543 if(sr
->headers
->ranges
[1]<=0 ||
544 sr
->headers
->ranges
[0]>sr
->headers
->ranges
[1])
552 if( (eq_pos
+1 != sep_pos
) && (len
== sep_pos
+ 1))
554 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+1, len
);
555 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);