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 | |
parent | d02c3ba12ae931e191924d44b2b3d4193058b2d9 (diff) | |
download | sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.tar.gz sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.tar.zst sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.zip |
HW4: 大致完成靜態網頁回應
-rw-r--r-- | hw4/Makefile.am | 19 | ||||
-rw-r--r-- | hw4/cgiprog/file_reader.c | 4 | ||||
-rw-r--r-- | hw4/cgiprog/server_info.c | 17 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-conn.c | 236 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-server.c | 41 | ||||
-rw-r--r-- | hw4/l4basic/l4str.c | 16 | ||||
-rw-r--r-- | hw4/l4basic/l4str.h | 1 |
7 files changed, 315 insertions, 19 deletions
diff --git a/hw4/Makefile.am b/hw4/Makefile.am index 0bbd28c..7539c62 100644 --- a/hw4/Makefile.am +++ b/hw4/Makefile.am @@ -1,11 +1,11 @@ -EXTRA_DIST = -NULL = +EXTRA_DIST = +NULL = bin_PROGRAMS = cgish-httpd noinst_LIBRARIES = libl4basic.a cgidir = $(datadir)/$(PACKAGE) -cgi_PROGRAMS = file_reader +cgi_PROGRAMS = file_reader server_info libl4basic_a_SOURCES = \ l4basic/memwrap.h \ @@ -24,7 +24,7 @@ libl4basic_a_SOURCES = \ $(NULL) libl4basic_a_CFLAGS = -pthread -I$(top_srcdir)/l4basic libl4basic_a_CPPFLAGS = -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 - + cgish_httpd_SOURCES = \ chttpd/chttpd-log.h \ chttpd/chttpd-log.c \ @@ -39,3 +39,14 @@ cgish_httpd_CFLAGS = -pthread -I$(top_srcdir)/l4basic -I$(top_srcdir)/chttpd cgish_httpd_LDADD = $(top_builddir)/libl4basic.a file_reader_SOURCES = cgiprog/file_reader.c +server_info_SOURCES = cgiprog/server_info.c + +# Workaround for automake (executable cannot named after `info') +MY_V_LN = $(MY_V_LN_@AM_V@) +MY_V_LN_ = $(MY_V_LN_@AM_DEFAULT_V@) +MY_V_LN_0 = @echo " LN " $@; + +all-local: $(abs_top_builddir)/info +$(abs_top_builddir)/info: $(top_builddir)/server_info + $(MY_V_LN)ln -s server_info $(abs_top_builddir)/info +CLEANFILES = $(abs_top_builddir)/info diff --git a/hw4/cgiprog/file_reader.c b/hw4/cgiprog/file_reader.c index dec8b30..a6f958f 100644 --- a/hw4/cgiprog/file_reader.c +++ b/hw4/cgiprog/file_reader.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L +#define NL "\015\012" #include <errno.h> #include <fcntl.h> #include <stdio.h> @@ -5,8 +7,6 @@ #include <string.h> #include <unistd.h> -#define NL "\015\012" - int main (int argc, char* argv[]) { char* qs = getenv ("QUERY_STRING"); if (qs == NULL || *qs == '\0') { diff --git a/hw4/cgiprog/server_info.c b/hw4/cgiprog/server_info.c new file mode 100644 index 0000000..a49022f --- /dev/null +++ b/hw4/cgiprog/server_info.c @@ -0,0 +1,17 @@ +#define _POSIX_C_SOURCE 200809L +#define NL "\015\012" +#include <sys/types.h> +#include <signal.h> +#include <stdio.h> +#include <unistd.h> + +int main (int argc, char* argv[]) { + kill (getppid (), SIGUSR1); + fputs ( + "HTTP/1.1 200 OK" NL + "Content-Type: text/html" NL NL + "<html><head><title>Info Request</title></head>" NL + "<body><p>Look at your console!</p>" + "</body></html>" NL, stdout); + return 0; +} 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: diff --git a/hw4/chttpd/chttpd-server.c b/hw4/chttpd/chttpd-server.c index ba1eea6..0106191 100644 --- a/hw4/chttpd/chttpd-server.c +++ b/hw4/chttpd/chttpd-server.c @@ -241,7 +241,10 @@ static void server_notify_setter (int signo) { server_notify = 1; } -static void dummy_handler (int signo) { } +static volatile sig_atomic_t server_info; +static void server_info_setter (int signo) { + server_info = 1; +} #define ERRLEN 256 static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { @@ -257,6 +260,7 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { sigaddset (&crit_mask, SIGINT); sigaddset (&crit_mask, SIGTERM); sigaddset (&crit_mask, SIGHUP); + sigaddset (&crit_mask, SIGUSR1); sigaddset (&crit_mask, SIGUSR2); pthread_attr_t padetach; @@ -285,6 +289,30 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { } } + /* Show info */ + if (server_info) { + server_info = 0; + pthread_rwlock_rdlock (&chttpd_server_count_lock); + printf ("%llu connections has been accepted\n", chttpd_server_count); + pthread_rwlock_unlock (&chttpd_server_count_lock); + + puts ("PID of running processes: (-1 means not available)"); + + unsigned c = 0; + LbsList* iter = slist->first; + for (; iter != NULL; iter = iter->next, c++) { + ChttpdServer* server = iter->data; + printf (" Server %u:", c); + pthread_rwlock_rdlock (&server->lock); + for (LbsList* ci = server->conn->first; ci != NULL; ci = ci->next) { + ChttpdConn* conn = ci->data; + printf (" %ld", (long)(conn->pid)); + } + pthread_rwlock_unlock (&server->lock); + putchar ('\n'); + } + } + /* Check whether there are notifications */ pthread_mutex_lock (&chttpd_server_notify_mutex); if (chttpd_server_notify || server_notify) { @@ -345,7 +373,7 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { count = chttpd_server_count; pthread_rwlock_unlock (&chttpd_server_count_lock); - chttpd_log_write (hlog, "%lld connections has been accepted", count); + chttpd_log_write (hlog, "%llu connections has been accepted", count); unsigned c = 0; for (iter = slist->first; iter != NULL; iter = iter->next, c++) { @@ -542,21 +570,16 @@ int main (int argc, char* argv[]) { sigemptyset (&sa_term.sa_mask); sigaction (SIGINT, &sa_term, NULL); /* Handled by main thread in loop */ sigaction (SIGTERM, &sa_term, NULL); /* Handled by main thread in loop */ - sa_usr1.sa_handler = dummy_handler; + sa_usr1.sa_handler = server_info_setter; sa_usr1.sa_flags = 0; sigemptyset (&sa_usr1.sa_mask); - sigaction (SIGUSR1, &sa_usr1, NULL); /* Handled by connection thread */ + sigaction (SIGUSR1, &sa_usr1, NULL); /* Handled by main thread in loop */ sa_usr2.sa_handler = server_notify_setter; sa_usr2.sa_flags = 0; sigemptyset (&sa_usr2.sa_mask); sigaction (SIGHUP, &sa_usr2, NULL); /* Handled by main thread to interrupt */ sigaction (SIGUSR2, &sa_usr2, NULL); /* Handled by main thread to interrupt */ - sigset_t main_mask; - sigemptyset (&main_mask); - sigaddset (&main_mask, SIGUSR1); - pthread_sigmask (SIG_BLOCK, &main_mask, NULL); - puts ("Starting " PACKAGE_NAME " version " PACKAGE_VERSION " ..."); LbsListMeta* slist = chttpd_main_init (hlog, argv[1]); diff --git a/hw4/l4basic/l4str.c b/hw4/l4basic/l4str.c index c1f6c51..5569f7a 100644 --- a/hw4/l4basic/l4str.c +++ b/hw4/l4basic/l4str.c @@ -7,6 +7,22 @@ #include <stdlib.h> #include <string.h> +bool lbs_str_has_prefix (const char* str, const char* prefix) { + int i; + for (i = 0; str != '\0' && prefix[i] != '\0'; i++) { + if (str[i] != prefix[i]) { + return false; + } + } + if (str[i] == '\0' && prefix[i] == '\0') { + return true; + } + if (str[i] == '\0') { + return false; + } + return true; +} + bool lbs_str_has_suffix (const char* str, const char* suffix) { size_t len = strlen (str); size_t suflen = strlen (suffix); diff --git a/hw4/l4basic/l4str.h b/hw4/l4basic/l4str.h index b041e22..a926798 100644 --- a/hw4/l4basic/l4str.h +++ b/hw4/l4basic/l4str.h @@ -8,6 +8,7 @@ #define LBS_STR_STATIC_STRLEN(x) (sizeof(x)/sizeof(char) - 1) #define LBS_STR_ARRAY_LEN(x,t) (sizeof(x)/sizeof(t)) +bool lbs_str_has_prefix (const char* str, const char* prefix); bool lbs_str_has_suffix (const char* str, const char* suffix); char* lbs_str_printf (const char* format, ...); char* lbs_str_vprintf (const char* format, va_list ap); |