diff options
author | LAN-TW <lantw44@gmail.com> | 2014-01-15 04:12:27 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2014-01-15 04:13:10 +0800 |
commit | 9d8903de3ca42b6daf2b716f2c1683430ca5e2b9 (patch) | |
tree | 6784169868dbbb4ea25356e2975e9167376247d4 /hw4/chttpd/chttpd-conn.c | |
parent | d02c3ba12ae931e191924d44b2b3d4193058b2d9 (diff) | |
download | sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.tar.gz sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.tar.zst sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.zip |
HW4: 大致完成靜態網頁回應
Diffstat (limited to 'hw4/chttpd/chttpd-conn.c')
-rw-r--r-- | hw4/chttpd/chttpd-conn.c | 236 |
1 files changed, 232 insertions, 4 deletions
diff --git a/hw4/chttpd/chttpd-conn.c b/hw4/chttpd/chttpd-conn.c index 538f079..41e1c79 100644 --- a/hw4/chttpd/chttpd-conn.c +++ b/hw4/chttpd/chttpd-conn.c @@ -15,11 +15,14 @@ #include <l4posix.h> #include <ctype.h> +#include <fcntl.h> #include <errno.h> #include <pthread.h> #include <signal.h> #include <stdlib.h> #include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #define NL "\015\012" @@ -27,15 +30,19 @@ enum { HTTP_STATUS_200_OK, HTTP_STATUS_400_BAD_REQUEST, + HTTP_STATUS_403_FORBIDDEN, HTTP_STATUS_404_NOT_FOUND, - HTTP_STATUS_500_INTERNAL_SERVER_ERROR + HTTP_STATUS_500_INTERNAL_SERVER_ERROR, + HTTP_STATUS_501_NOT_IMPLEMENTED }; static char* http_status_hdr[] = { [HTTP_STATUS_200_OK] = "HTTP/1.1 200 OK" NL, [HTTP_STATUS_400_BAD_REQUEST] = "HTTP/1.1 400 Bad Request" NL, + [HTTP_STATUS_403_FORBIDDEN] = "HTTP/1.1 403 Forbidden" NL, [HTTP_STATUS_404_NOT_FOUND] = "HTTP/1.1 404 Not Found" NL, - [HTTP_STATUS_500_INTERNAL_SERVER_ERROR] = "HTTP/1.1 500 Internal Server Error" NL + [HTTP_STATUS_500_INTERNAL_SERVER_ERROR] = "HTTP/1.1 500 Internal Server Error" NL, + [HTTP_STATUS_501_NOT_IMPLEMENTED] = "HTTP/1.1 501 Not Implemented" NL }; static char* http_status_msg[] = { @@ -44,12 +51,37 @@ static char* http_status_msg[] = { "<body><p>You browser sent a broken HTTP request." NL "<a href=\"http://tools.ietf.org/search/rfc2616#section-10.4.1\">" "[400]</a></p>" + "</body></html>" NL, + [HTTP_STATUS_403_FORBIDDEN] = + "\n<html><head><title>403 Forbidden</title></head>" NL + "<body><p>You do not have permission to access this object." NL + "<a href=\"http://tools.ietf.org/search/rfc2616#section-10.4.4\">" + "[403]</a></p>" + "</body></html>" NL, + [HTTP_STATUS_404_NOT_FOUND] = + "\n<html><head><title>400 Not Found</title></head>" NL + "<body><p>Request resource cannot be found on this server." NL + "<a href=\"http://tools.ietf.org/search/rfc2616#section-10.4.5\">" + "[404]</a></p>" + "</body></html>" NL, + [HTTP_STATUS_500_INTERNAL_SERVER_ERROR] = + "\n<html><head><title>500 Internal Server Error</title></head>" NL + "<body><p>Server encountered an unexpected condition." NL + "Please contact the administrator of this site." NL + "<a href=\"http://tools.ietf.org/search/rfc2616#section-10.5.1\">" + "[500]</a></p>" + "</body></html>" NL, + [HTTP_STATUS_501_NOT_IMPLEMENTED] = + "\n<html><head><title>501 Not Implemented</title></head>" NL + "<body><p>Sorry, I don't know howto handle this method..." NL + "<a href=\"http://tools.ietf.org/search/rfc2616#section-10.5.2\">" + "[501]</a></p>" "</body></html>" NL }; static inline void http_status_write (int connfd, int status_enum) { lbs_posix_write_all (connfd, http_status_hdr[status_enum], 0); - lbs_posix_write_all (connfd, NL, 2); + lbs_posix_write_all (connfd, "Content-Type: text/html" NL NL, 0); lbs_posix_write_all (connfd, http_status_msg[status_enum], 0); } @@ -60,7 +92,7 @@ static inline void http_status_write (int connfd, int status_enum) { unsigned long long id = conn->id; \ int connfd = conn->connfd; \ pthread_sigmask (SIG_SETMASK, &server->conn_mask, NULL); - + #define CHTTPD_CONN_THREAD_DESTROY \ chttpd_log_write (hlog, "[%4llu] terminated", conn->id); \ pthread_rwlock_wrlock (&server->lock); \ @@ -90,6 +122,40 @@ static inline char* internal_memstr (const void* haystack, return NULL; } +static inline void internal_setenv (LbsStrv* strv, + const char* var, const char* value) { + + LbsArray* str_wrapper = NULL; + char* equ_loc = NULL; + size_t var_len = strlen (var); + size_t value_len = strlen (value); + + for (size_t i = 0; i < lbs_strv_get_len (strv); i++) { + size_t slen = lbs_strv_get_str_len (strv, i); + char* str_no_null = lbs_strv_get_str_not_null_terminated (strv, i); + char* str_equ_loc = memchr (str_no_null, '=', slen); + if (str_equ_loc != NULL && var_len < slen && + strncmp (str_no_null, var, var_len)) { + str_wrapper = lbs_strv_get_str_wrapper (strv, i); + equ_loc = str_equ_loc; + break; + } + } + + if (str_wrapper != NULL && equ_loc != NULL) { + /* Use existing string */ + lbs_array_set_len (str_wrapper, var_len + 1); + lbs_array_append_mass (str_wrapper, value, value_len); + } else { + /* Append new string */ + lbs_strv_append_str_empty (strv); + str_wrapper = lbs_strv_get_str_wrapper (strv, lbs_strv_get_len (strv) - 1); + lbs_array_append_mass (str_wrapper, var, var_len); + lbs_array_append_data (str_wrapper, &(char){ '=' }); + lbs_array_append_mass (str_wrapper, value, value_len); + } +} + #define ERRLEN 256 void* chttpd_conn_admin (void* ptr_to_ChttpdConn) { CHTTPD_CONN_THREAD_INIT; @@ -199,6 +265,168 @@ void* chttpd_conn_http (void* ptr_to_ChttpdConn) { } #endif + /* Only GET is implemented */ + if (strcmp (request_method, "GET")) { + chttpd_log_write (hlog, "[%4llu] 501 Not Implemented: unknown" + "method `%s\'", id, request_method); + http_status_write (connfd, HTTP_STATUS_501_NOT_IMPLEMENTED); + goto http_exit; + } + + int pipe_in[2], pipe_out[2], pipe_exec[2]; + if (pipe (pipe_in) < 0) { + chttpd_log_write (hlog, "[%4llu] 500 Internal Server Error:" + "pipe: %s", id, get_errmsg (errno, errmsg, ERRLEN)); + http_status_write (connfd, HTTP_STATUS_500_INTERNAL_SERVER_ERROR); + goto http_exit; + } + if (pipe (pipe_out) < 0) { + chttpd_log_write (hlog, "[%4llu] 500 Internal Server Error:" + "pipe: %s", id, get_errmsg (errno, errmsg, ERRLEN)); + http_status_write (connfd, HTTP_STATUS_500_INTERNAL_SERVER_ERROR); + close (pipe_in[0]); + close (pipe_in[1]); + goto http_exit; + } + if (pipe (pipe_exec) < 0) { + chttpd_log_write (hlog, "[%4llu] 500 Internal Server Error:" + "pipe: %s", id, get_errmsg (errno, errmsg, ERRLEN)); + http_status_write (connfd, HTTP_STATUS_500_INTERNAL_SERVER_ERROR); + close (pipe_in[0]); + close (pipe_in[1]); + close (pipe_out[0]); + close (pipe_out[1]); + goto http_exit; + } + + extern char** environ; + + LbsStrv* cgienv = lbs_strv_new (); + lbs_strv_append_strv (cgienv, LBS_STRV_GENERIC_CONST (environ)); + internal_setenv (cgienv, "GATEWAY_INTERFACE", "CGI/1.1"); + internal_setenv (cgienv, "QUERY_STRING", query_string); + internal_setenv (cgienv, "REQUEST_METHOD", request_method); + internal_setenv (cgienv, "REQUEST_URI", request_uri - 1); + internal_setenv (cgienv, "SERVER_PROTOCOL", server_protocol); + char** cgienv_out = lbs_strv_drop_struct (cgienv); + + pid_t pid = fork (); + if (pid < 0) { + chttpd_log_write (hlog, "[%4llu] fork: %s", id, + get_errmsg (errno, errmsg, ERRLEN)); + lbs_strv_generic_free (cgienv_out); + http_status_write (connfd, HTTP_STATUS_500_INTERNAL_SERVER_ERROR); + close (pipe_in[0]); + close (pipe_in[1]); + close (pipe_out[0]); + close (pipe_out[1]); + close (pipe_exec[0]); + close (pipe_exec[1]); + goto http_exit; + } else if (pid > 0) { + close (pipe_in[0]); + close (pipe_out[1]); + close (pipe_exec[1]); + lbs_strv_generic_free (cgienv_out); + + int errnum; + int statfd; + struct stat st; + if (read (pipe_exec[0], &errnum, sizeof (int)) > 0) { + switch (errnum) { + case ENOENT: // File not found + chttpd_log_write (hlog, "[%4llu] 404 Not Found", id); + http_status_write (connfd, HTTP_STATUS_404_NOT_FOUND); + goto http_exit; + case EACCES: // Permission denied (static file) + statfd = open (request_uri, O_RDONLY); + if (statfd < 0) { + switch (errno) { + case ENOENT: + chttpd_log_write (hlog, "[%4llu] (static) " + "404 Not Found", id); + http_status_write (connfd, + HTTP_STATUS_404_NOT_FOUND); + goto http_exit; + case EACCES: + chttpd_log_write (hlog, "[%4llu] (static) " + "403 Forbidden", id); + http_status_write (connfd, + HTTP_STATUS_403_FORBIDDEN); + goto http_exit; + default: + chttpd_log_write (hlog, "[%4llu] (static) " + "500 Internal Server Error: open: %s", + id, get_errmsg (errno, errmsg, ERRLEN)); + goto http_exit; + } + } + if (fstat (statfd, &st) < 0) { + chttpd_log_write (hlog, "[%4llu] (static) " + "500 Internal Server Error: fstat: %s", + id, get_errmsg (errno, errmsg, ERRLEN)); + close (statfd); + goto http_exit; + } + + off_t written = 0; + off_t total = st.st_size; + char* content_hdr = lbs_str_printf ( + "Content-Type: application/octet-stream" NL + "Content-Length: %lld" NL NL, (long long)total); + lbs_posix_write_all (connfd, http_status_hdr[HTTP_STATUS_200_OK], 0); + lbs_posix_write_all (connfd, content_hdr, 0); + free (content_hdr); + + chttpd_log_write (hlog, "[%4llu] (static) 200 OK: " + "Content-Length: %lld", id, (long long)total); + while (written < total) { + ssize_t r = read (statfd, conn->buf, CHTTPD_CONN_BUF_SIZE); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } else { + chttpd_log_write (hlog, "[%4llu] (static) " + "read: %s", id, get_errmsg (errno, errmsg, ERRLEN)); + break; + } + } else if (r == 0) { + break; + } + lbs_posix_write_all (connfd, conn->buf, r); + written += r; + } + chttpd_log_write (hlog, "[%4llu] (static) %lld bytes written", + id, (long long)written); + goto http_exit; + default: + chttpd_log_write (hlog, "[%4llu] 500 Internal Server Error: " + "execve: %s", id, get_errmsg (errnum, errmsg, ERRLEN)); + http_status_write (connfd, HTTP_STATUS_500_INTERNAL_SERVER_ERROR); + goto http_exit; + } + } + } else { + close (pipe_in[1]); + while (dup2 (pipe_in[0], 0) < 0); + close (pipe_in[0]); + + close (pipe_out[0]); + while (dup2 (pipe_out[1], 1) < 0); + close (pipe_out[1]); + + while (lbs_posix_add_fd (pipe_exec[1], FD_CLOEXEC) < 0); + close (pipe_exec[0]); + + char* cgiargv[] = { request_uri, NULL }; + execve (request_uri, cgiargv, cgienv_out); + + int errnum = errno; + write (pipe_exec[1], &errnum, sizeof (int)); + _exit (127); + } + + http_exit: |