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.
26 #include <sys/types.h>
33 #include "http_status.h"
45 int mk_http_method_check(mk_pointer method
)
47 if (strncmp(method
.data
, HTTP_METHOD_GET_STR
, method
.len
) == 0) {
48 return HTTP_METHOD_GET
;
51 if (strncmp(method
.data
, HTTP_METHOD_POST_STR
, method
.len
) == 0) {
52 return HTTP_METHOD_POST
;
55 if (strncmp(method
.data
, HTTP_METHOD_HEAD_STR
, method
.len
) == 0) {
56 return HTTP_METHOD_HEAD
;
59 return METHOD_NOT_FOUND
;
62 mk_pointer
mk_http_method_check_str(int method
)
66 return mk_http_method_get_p
;
68 case HTTP_METHOD_POST
:
69 return mk_http_method_post_p
;
71 case HTTP_METHOD_HEAD
:
72 return mk_http_method_head_p
;
74 return mk_http_method_null_p
;
77 int mk_http_method_get(char *body
)
79 int int_method
, pos
= 0;
80 int max_len_method
= 5;
83 /* Max method length is 4 (POST/HEAD) */
84 pos
= mk_string_char_search(body
, ' ', 5);
85 if (pos
<= 2 || pos
>= max_len_method
) {
86 return METHOD_NOT_FOUND
;
90 method
.len
= (unsigned long) pos
;
92 int_method
= mk_http_method_check(method
);
97 int mk_http_protocol_check(char *protocol
, int len
)
99 if (strncmp(protocol
, HTTP_PROTOCOL_11_STR
, len
) == 0) {
100 return HTTP_PROTOCOL_11
;
102 if (strncmp(protocol
, HTTP_PROTOCOL_10_STR
, len
) == 0) {
103 return HTTP_PROTOCOL_10
;
105 if (strncmp(protocol
, HTTP_PROTOCOL_09_STR
, len
) == 0) {
106 return HTTP_PROTOCOL_09
;
109 return HTTP_PROTOCOL_UNKNOWN
;
112 mk_pointer
mk_http_protocol_check_str(int protocol
)
114 if (protocol
== HTTP_PROTOCOL_11
) {
115 return mk_http_protocol_11_p
;
117 if (protocol
== HTTP_PROTOCOL_10
) {
118 return mk_http_protocol_10_p
;
120 if (protocol
== HTTP_PROTOCOL_09
) {
121 return mk_http_protocol_09_p
;
124 return mk_http_protocol_null_p
;
127 int mk_http_init(struct client_request
*cr
, struct request
*sr
)
131 struct mimetype
*mime
;
132 char *uri_data
= NULL
;
136 MK_TRACE("HTTP Protocol Init");
139 /* Normal request default site */
140 if ((strcmp(sr
->uri_processed
, "/")) == 0) {
141 sr
->real_path
.data
= mk_string_dup(sr
->host_conf
->documentroot
.data
);
142 sr
->real_path
.len
= sr
->host_conf
->documentroot
.len
;
146 if (sr
->uri_processed
) {
147 uri_data
= sr
->uri_processed
;
148 uri_len
= strlen(sr
->uri_processed
);
151 uri_data
= sr
->uri
.data
;
152 uri_len
= sr
->uri
.len
;
155 /* Compose real path */
156 if (sr
->user_home
== VAR_OFF
) {
157 ret
= mk_buffer_cat(&sr
->real_path
,
158 sr
->host_conf
->documentroot
.data
,
159 sr
->host_conf
->documentroot
.len
,
165 MK_TRACE("Error composing real path");
171 /* Check backward directory request */
172 if (mk_string_search_n(uri_data
,
173 HTTP_DIRECTORY_BACKWARD
,
175 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
179 sr
->file_info
= mk_file_get_info(sr
->real_path
.data
);
181 if (!sr
->file_info
) {
182 /* if the resource requested doesn't exists, let's
183 * check if some plugin would like to handle it
186 MK_TRACE("No file, look for handler plugin");
188 ret
= mk_plugin_stage_run(MK_PLUGIN_STAGE_30
, cr
->socket
, NULL
, cr
, sr
);
189 if (ret
== MK_PLUGIN_RET_CLOSE_CONX
) {
190 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
193 else if (ret
== MK_PLUGIN_RET_CONTINUE
) {
194 return MK_PLUGIN_RET_CONTINUE
;
196 else if (ret
== MK_PLUGIN_RET_END
) {
200 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
);
204 /* is it a valid directory ? */
205 if (sr
->file_info
->is_directory
== MK_FILE_TRUE
) {
206 /* Send redirect header if end slash is not found */
207 if (mk_http_directory_redirect_check(cr
, sr
) == -1) {
209 MK_TRACE("Directory Redirect");
211 /* Redirect has been sent */
215 /* looking for a index file */
216 mk_pointer index_file
;
217 index_file
= mk_request_index(sr
->real_path
.data
);
219 if (index_file
.data
) {
220 mk_mem_free(sr
->file_info
);
221 mk_pointer_free(&sr
->real_path
);
223 sr
->real_path
= index_file
;
224 sr
->file_info
= mk_file_get_info(sr
->real_path
.data
);
228 /* Check symbolic link file */
229 if (sr
->file_info
->is_link
== MK_FILE_TRUE
) {
230 if (config
->symlink
== VAR_OFF
) {
231 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
236 char linked_file
[MAX_PATH
];
237 n
= readlink(sr
->real_path
.data
, linked_file
, MAX_PATH
);
241 /* Plugin Stage 30: look for handlers for this request */
242 ret
= mk_plugin_stage_run(MK_PLUGIN_STAGE_30
, 0, NULL
, cr
, sr
);
244 MK_TRACE("STAGE_30 returned %i", ret
);
246 if (ret
== MK_PLUGIN_RET_CLOSE_CONX
) {
247 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
250 else if (ret
== MK_PLUGIN_RET_CONTINUE
) {
251 return MK_PLUGIN_RET_CONTINUE
;
253 else if (ret
== MK_PLUGIN_RET_END
) {
257 /* read permissions and check file */
258 if (sr
->file_info
->read_access
== MK_FILE_FALSE
) {
259 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
263 /* Matching MimeType */
264 mime
= mk_mimetype_find(&sr
->real_path
);
266 mime
= mimetype_default
;
269 if (sr
->file_info
->is_directory
== MK_FILE_TRUE
) {
270 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
275 if (sr
->file_info
->size
< 0) {
276 mk_request_error(M_CLIENT_NOT_FOUND
, cr
, sr
);
280 /* counter connections */
281 sr
->headers
->pconnections_left
= (int)
282 (config
->max_keep_alive_request
- cr
->counter_connections
);
285 sr
->headers
->last_modified
= sr
->file_info
->last_modification
;
287 if (sr
->if_modified_since
.data
&& sr
->method
== HTTP_METHOD_GET
) {
288 time_t date_client
; /* Date sent by client */
289 time_t date_file_server
; /* Date server file */
291 date_client
= PutDate_unix(sr
->if_modified_since
.data
);
292 date_file_server
= sr
->file_info
->last_modification
;
294 if ((date_file_server
<= date_client
) && (date_client
> 0)) {
295 mk_header_set_http_status(sr
, M_NOT_MODIFIED
);
296 mk_header_send(cr
->socket
, cr
, sr
);
300 mk_header_set_http_status(sr
, M_HTTP_OK
);
301 sr
->headers
->cgi
= SH_NOCGI
;
302 sr
->headers
->location
= NULL
;
304 /* Object size for log and response headers */
305 sr
->headers
->content_length
= sr
->file_info
->size
;
306 sr
->headers
->real_length
= sr
->file_info
->size
;
308 /* Process methods */
309 if (sr
->method
== HTTP_METHOD_GET
|| sr
->method
== HTTP_METHOD_POST
) {
310 sr
->headers
->content_type
= mime
->type
;
312 if (sr
->range
.data
!= NULL
&& config
->resume
== VAR_ON
) {
313 if (mk_http_range_parse(sr
) < 0) {
314 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, sr
);
317 if (sr
->headers
->ranges
[0] >= 0 || sr
->headers
->ranges
[1] >= 0) {
318 mk_header_set_http_status(sr
, M_HTTP_PARTIAL
);
323 /* without content-type */
324 mk_pointer_reset(&sr
->headers
->content_type
);
328 if (sr
->file_info
->size
> 0) {
329 sr
->fd_file
= open(sr
->real_path
.data
, config
->open_flags
);
330 if (sr
->fd_file
== -1) {
332 MK_TRACE("open() failed");
334 mk_request_error(M_CLIENT_FORBIDDEN
, cr
, sr
);
340 mk_header_send(cr
->socket
, cr
, sr
);
342 if (sr
->headers
->content_length
== 0) {
346 /* Send file content*/
347 if (sr
->method
== HTTP_METHOD_GET
|| sr
->method
== HTTP_METHOD_POST
) {
348 /* Calc bytes to send & offset */
349 if (mk_http_range_set(sr
, sr
->file_info
->size
) != 0) {
350 mk_request_error(M_CLIENT_BAD_REQUEST
, cr
, sr
);
354 bytes
= mk_http_send_file(cr
, sr
);
360 int mk_http_send_file(struct client_request
*cr
, struct request
*sr
)
364 nbytes
= mk_socket_send_file(cr
->socket
, sr
->fd_file
,
365 &sr
->bytes_offset
, sr
->bytes_to_send
);
369 mk_socket_set_cork_flag(cr
->socket
, TCP_CORK_OFF
);
371 sr
->bytes_to_send
-= nbytes
;
375 return sr
->bytes_to_send
;
378 int mk_http_directory_redirect_check(struct client_request
*cr
,
383 char *real_location
= 0;
387 * We have to check if exist an slash to the end of
388 * this string, if doesn't exist we send a redirection header
390 if (sr
->uri_processed
[strlen(sr
->uri_processed
) - 1] == '/') {
394 host
= mk_pointer_to_buf(sr
->host
);
396 mk_string_build(&location
, &len
, "%s/", sr
->uri_processed
);
397 if (config
->serverport
== config
->standard_port
) {
398 mk_string_build(&real_location
, &len
, "http://%s%s", host
, location
);
401 mk_string_build(&real_location
, &len
, "http://%s:%i%s",
402 host
, config
->serverport
, location
);
406 MK_TRACE("Redirecting to '%s'", real_location
);
411 mk_header_set_http_status(sr
, M_REDIR_MOVED
);
412 sr
->headers
->content_length
= 0;
414 mk_pointer_reset(&sr
->headers
->content_type
);
415 sr
->headers
->location
= real_location
;
416 sr
->headers
->cgi
= SH_NOCGI
;
417 sr
->headers
->pconnections_left
=
418 (config
->max_keep_alive_request
- cr
->counter_connections
);
420 mk_header_send(cr
->socket
, cr
, sr
);
421 mk_socket_set_cork_flag(cr
->socket
, TCP_CORK_OFF
);
424 * we do not free() real_location
425 * as it's freed by iov
427 mk_mem_free(location
);
428 sr
->headers
->location
= NULL
;
433 * Check if a connection can continue open using as criteria
434 * the keepalive headers vars and Monkey configuration
436 int mk_http_keepalive_check(int socket
, struct client_request
*cr
)
442 if (config
->keep_alive
== VAR_OFF
|| cr
->request
->keep_alive
== VAR_OFF
) {
446 if (cr
->counter_connections
>= config
->max_keep_alive_request
) {
453 int mk_http_range_set(struct request
*sr
, long file_size
)
455 struct header_values
*sh
= sr
->headers
;
457 sr
->bytes_to_send
= file_size
;
458 sr
->bytes_offset
= 0;
460 if (config
->resume
== VAR_ON
&& sr
->range
.data
) {
462 if (sh
->ranges
[0] >= 0 && sh
->ranges
[1] == -1) {
463 sr
->bytes_offset
= sh
->ranges
[0];
464 sr
->bytes_to_send
= file_size
- sr
->bytes_offset
;
468 if (sh
->ranges
[0] >= 0 && sh
->ranges
[1] >= 0) {
469 sr
->bytes_offset
= sh
->ranges
[0];
470 sr
->bytes_to_send
= labs(sh
->ranges
[1] - sh
->ranges
[0]) + 1;
474 if (sh
->ranges
[0] == -1 && sh
->ranges
[1] > 0) {
475 sr
->bytes_to_send
= sh
->ranges
[1];
476 sr
->bytes_offset
= file_size
- sh
->ranges
[1];
479 if (sr
->bytes_offset
> file_size
|| sr
->bytes_to_send
> file_size
) {
483 lseek(sr
->fd_file
, sr
->bytes_offset
, SEEK_SET
);
490 int mk_http_range_parse(struct request
*sr
)
492 int eq_pos
, sep_pos
, len
;
494 struct header_values
*sh
;
499 if ((eq_pos
= mk_string_search_n(sr
->range
.data
, "=", sr
->range
.len
)) < 0)
502 if (strncasecmp(sr
->range
.data
, "Bytes", eq_pos
) != 0)
505 if ((sep_pos
= mk_string_search_n(sr
->range
.data
, "-", sr
->range
.len
)) < 0)
512 if (eq_pos
+ 1 == sep_pos
) {
514 sh
->ranges
[1] = (unsigned long) atol(sr
->range
.data
+ sep_pos
+ 1);
516 if (sh
->ranges
[1] <= 0) {
520 sh
->content_length
= sh
->ranges
[1];
525 if ((eq_pos
+ 1 != sep_pos
) && (len
> sep_pos
+ 1)) {
526 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+ 1, sep_pos
);
527 sh
->ranges
[0] = (unsigned long) atol(buffer
);
530 buffer
= mk_string_copy_substr(sr
->range
.data
, sep_pos
+ 1, len
);
531 sh
->ranges
[1] = (unsigned long) atol(buffer
);
534 if (sh
->ranges
[1] <= 0 || (sh
->ranges
[0] > sh
->ranges
[1])) {
538 sh
->content_length
= abs(sh
->ranges
[1] - sh
->ranges
[0]) + 1;
542 if ((eq_pos
+ 1 != sep_pos
) && (len
== sep_pos
+ 1)) {
543 buffer
= mk_string_copy_substr(sr
->range
.data
, eq_pos
+ 1, len
);
544 sr
->headers
->ranges
[0] = (unsigned long) atol(buffer
);
547 sh
->content_length
= (sh
->content_length
- sh
->ranges
[0]);
555 * Check if client request still has pending data
557 * Return 0 when all expected data has arrived or -1 when
558 * the connection is on a pending status due to HTTP spec
560 * This function is called from request.c :: mk_handler_read(..)
562 int mk_http_pending_request(struct client_request
*cr
)
567 if (cr
->body_length
>= mk_endblock
.len
) {
568 end
= (cr
->body
+ cr
->body_length
) - mk_endblock
.len
;
575 /* try to match CRLF at the end of the request */
576 if (cr
->body_pos_end
< 0) {
577 if (strncmp(end
, mk_endblock
.data
, mk_endblock
.len
) == 0) {
578 cr
->body_pos_end
= cr
->body_length
- mk_endblock
.len
;
580 else if ((n
= mk_string_search(cr
->body
, mk_endblock
.data
)) >= 0 ){
582 cr
->body_pos_end
= n
;
589 if (cr
->first_method
== HTTP_METHOD_UNKNOWN
) {
590 cr
->first_method
= mk_http_method_get(cr
->body
);
593 if (cr
->first_method
== HTTP_METHOD_POST
) {
594 if (cr
->body_pos_end
> 0) {
595 /* if first block has ended, we need to verify if exists
596 * a previous block end, that will means that the POST
597 * method has sent the whole information.
598 * just for ref: pipelining is not allowed with POST
600 if (cr
->body_pos_end
== cr
->body_length
- mk_endblock
.len
) {
601 /* Content-length is required, if is it not found,
602 * we pass as successfull in order to raise the error
605 if (mk_method_post_content_length(cr
->body
) < 0) {
606 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
611 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
620 cr
->status
= MK_REQUEST_STATUS_COMPLETED
;
624 mk_pointer
*mk_http_status_get(short int code
)
628 l
= mk_http_status_list
;
630 if (l
->index
== code
) {
641 void mk_http_status_add(short int val
[2])
645 mk_list_sint_t
*list
, *new;
647 for (i
= val
[0]; i
<= val
[1]; i
++) {
649 new = mk_mem_malloc(sizeof(mk_list_sint_t
));
653 str_val
= mk_mem_malloc(6);
654 snprintf(str_val
, len
- 1, "%i", i
);
656 new->value
.data
= str_val
;
659 if (!mk_http_status_list
) {
660 mk_http_status_list
= new;
663 list
= mk_http_status_list
;
673 void mk_http_status_list_init()
676 short int success
[2] = { 200, 206 };
677 short int redirections
[2] = { 300, 305 };
678 short int client_errors
[2] = { 400, 415 };
679 short int server_errors
[2] = { 500, 505 };
681 mk_http_status_add(success
);
682 mk_http_status_add(redirections
);
683 mk_http_status_add(client_errors
);
684 mk_http_status_add(server_errors
);
687 int mk_http_request_end(int socket
)
690 struct client_request
*cr
;
691 struct sched_list_node
*sched
;
693 sched
= mk_sched_get_thread_conf();
694 cr
= mk_request_client_get(socket
);
698 MK_TRACE("[FD %i] Not found", socket
);
705 MK_TRACE("Could not find sched list node :/");
710 /* We need to ask to http_keepalive if this
711 * connection can continue working or we must
714 ka
= mk_http_keepalive_check(socket
, cr
);
715 mk_request_free_list(cr
);
719 MK_TRACE("[FD %i] No KeepAlive mode, remove", cr
->socket
);
721 mk_request_client_remove(socket
);
724 mk_request_ka_next(cr
);
725 mk_epoll_change_mode(sched
->epoll_fd
,
726 socket
, MK_EPOLL_READ
);