summaryrefslogtreecommitdiffstats
path: root/hw4/chttpd/chttpd-conn.c
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 /hw4/chttpd/chttpd-conn.c
parentd02c3ba12ae931e191924d44b2b3d4193058b2d9 (diff)
downloadsp2013-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.c236
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: