diff options
Diffstat (limited to 'src/handler.c')
-rw-r--r-- | src/handler.c | 155 |
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; +} |