summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2014-01-15 04:12:27 +0800
committerLAN-TW <lantw44@gmail.com>2014-01-15 04:13:10 +0800
commit9d8903de3ca42b6daf2b716f2c1683430ca5e2b9 (patch)
tree6784169868dbbb4ea25356e2975e9167376247d4
parentd02c3ba12ae931e191924d44b2b3d4193058b2d9 (diff)
downloadsp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.tar.gz
sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.tar.zst
sp2013-9d8903de3ca42b6daf2b716f2c1683430ca5e2b9.zip
HW4: 大致完成靜態網頁回應
-rw-r--r--hw4/Makefile.am19
-rw-r--r--hw4/cgiprog/file_reader.c4
-rw-r--r--hw4/cgiprog/server_info.c17
-rw-r--r--hw4/chttpd/chttpd-conn.c236
-rw-r--r--hw4/chttpd/chttpd-server.c41
-rw-r--r--hw4/l4basic/l4str.c16
-rw-r--r--hw4/l4basic/l4str.h1
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);