summaryrefslogtreecommitdiffstats
path: root/src/handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/handler.c')
-rw-r--r--src/handler.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/handler.c b/src/handler.c
new file mode 100644
index 0000000..9bcec71
--- /dev/null
+++ b/src/handler.c
@@ -0,0 +1,155 @@
+#include "core.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/sendfile.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <errno.h>
+
+static int tip_client_method_not_found(struct tip_client *cli)
+{
+ /* To meet RFC 7231, this function MUST generate an Allow header field
+ * containing the correct methods. For example: "Allow: POST\r\n"
+ */
+ char buf[] = "HTTP/1.1 405 Method Not Allowed\r\n"
+ "Content-Length: 0\r\n\r\n";
+
+ send(tip_client_socket(cli), buf, strlen(buf), 0);
+
+ return -1;
+}
+
+static int tip_client_file_not_found(struct tip_client *cli)
+{
+ char buf[] = "HTTP/1.1 404 Not Found\r\n"
+ "Content-Length: 0\r\n\r\n";
+
+ send(tip_client_socket(cli), buf, strlen(buf), 0);
+
+ return -1;
+}
+
+/* TODO: sanitize uri, don't escape directory serving files. */
+static bool sanitize(const char *uri)
+{
+ return true;
+}
+
+#define BLOCK 1024000
+
+int tip_client_state_process_payload(struct tip_client *cli)
+{
+ const char *trailer, *x_redirect;
+ enum tip_http_method method;
+ bool allow_redirect = true;
+ char _uri[32], *uri = _uri;
+ char path[PATH_MAX + 1];
+ char redirect[5];
+ struct stat st;
+ int err;
+
+/* syslog(LOG_DEBUG, "%s:rhu %.32s ...\n",
+ inet_ntoa(cli->addr.sin_addr),
+ ntohs(cli->addr.sin_port), cli->buf); */
+
+ if (!strncmp(cli->buf, "GET", strlen("GET"))) {
+ method = TIP_METHOD_GET;
+ if (sscanf(cli->buf, "GET %31s HTTP/1.1", uri) != 1)
+ return tip_client_method_not_found(cli);
+ } else {
+ return tip_client_method_not_found(cli);
+ }
+
+ x_redirect = strstr(cli->buf, "X-Accept-Redirect: ");
+ if (x_redirect &&
+ sscanf(x_redirect, "X-Accept-Redirect: %4s", redirect) == 1 &&
+ !strncmp(redirect, "off", strlen("off")))
+ allow_redirect = false;
+
+ trailer = strstr(cli->buf, "\r\n\r\n");
+
+ if (!sanitize(uri))
+ return tip_client_method_not_found(cli);
+
+ snprintf(path, PATH_MAX, "%s/%s", root, uri);
+
+ err = stat(path, &st);
+ if (err < 0)
+ return tip_client_file_not_found(cli);
+
+ /* skip initial / */
+ uri++;
+
+ cli->uri = strdup(uri);
+ cli->path = strdup(path);
+ cli->size = st.st_size;
+ cli->allow_redirect = allow_redirect;
+
+ num_clients++;
+ if (cli->size > FILE_SIZE_THRESHOLD && num_clients > max_clients) {
+ if (!tip_client_redirect(cli)) {
+ tip_client_pending(cli);
+ return 1;
+ }
+ }
+
+ cli->state = TIP_CLIENT_PROCESSING_REQUEST_2;
+
+ return 0;
+}
+
+int tip_client_state_process_payload_reply(struct tip_client *cli)
+{
+ char buf[1024];
+ int fd;
+
+ if (cli->redirect) {
+ snprintf(buf, sizeof(buf),
+ "HTTP/1.1 301 Moves Permanently\r\nLocation: http://%s:%hu/%s\r\n\r\n",
+ inet_ntoa(cli->redirect_addr.sin_addr),
+ htons(cli->redirect_addr.sin_port), cli->uri);
+ send(tip_client_socket(cli), buf, strlen(buf), 0);
+
+ return 1;
+ }
+
+ fd = open(cli->path, O_RDONLY);
+ if (fd < 0)
+ return tip_client_file_not_found(cli);
+
+ snprintf(buf, sizeof(buf),
+ "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n",
+ cli->size);
+
+ send(tip_client_socket(cli), buf, strlen(buf), 0);
+
+ cli->fd = fd;
+ cli->state = TIP_CLIENT_PROCESSING_REQUEST_3;
+
+ return 0;
+}
+
+int tip_client_state_process_payload_bulk(struct tip_client *cli)
+{
+ sendfile(tip_client_socket(cli), cli->fd, &cli->offset, BLOCK);
+
+ if (cli->offset >= cli->size)
+ return 1;
+
+ return 0;
+}