@@ -33,10 +33,23 @@ size_t len; char *lastmod; char lastmod_b[64]; char *type; }; + +/* Request variables */ +struct filed_http_request { + /** Buffers **/ + struct filed_fileinfo fileinfo; + char path_b[1010]; + char tmpbuf[1010]; + + /** HTTP Request information **/ + char *path; /*** Path being requested ***/ + off_t offset; /*** Range start ***/ + ssize_t length; /*** Range length ***/ +}; /* Global variables */ struct filed_fileinfo *filed_fileinfo_fdcache; unsigned int filed_fileinfo_fdcache_size = CACHE_SIZE; @@ -103,11 +116,11 @@ return(fd); } /* Log a message */ -#define FILED_DONT_LOG +//#define FILED_DONT_LOG #ifdef FILED_DONT_LOG # define filed_logging_thread_init() 0 # define filed_log_msg_debug(x, ...) /**/ # define filed_log_msg(x) /**/ #else @@ -235,15 +248,28 @@ return(buffer); } /* Process an HTTP request and return the path requested */ -static char *filed_get_http_request(FILE *fp, char *buffer, size_t buffer_len) { +static struct filed_http_request *filed_get_http_request(FILE *fp, struct filed_http_request *buffer_st) { char *method, *path; - char tmpbuf[1010]; + char *buffer, *tmpbuffer, *workbuffer, *workbuffer_next; + size_t buffer_len, tmpbuffer_len; + off_t range_start, range_end; + ssize_t range_length; int i; + range_start = 0; + range_end = 0; + range_length = -1; + + buffer = buffer_st->path_b; + buffer_len = sizeof(buffer_st->path_b); + + tmpbuffer = buffer_st->tmpbuf; + tmpbuffer_len = sizeof(buffer_st->tmpbuf); + filed_log_msg("WAIT_FOR_REQUEST FD=..."); fgets(buffer, buffer_len, fp); method = buffer; @@ -269,12 +295,33 @@ filed_log_msg("GOT_REQUEST FD=... PATH=..."); filed_log_msg("WAIT_FOR_HEADERS FD=..."); for (i = 0; i < 100; i++) { - fgets(tmpbuf, sizeof(tmpbuf), fp); - if (memcmp(tmpbuf, "\r\n", 2) == 0) { + fgets(tmpbuffer, tmpbuffer_len, fp); + + if (strncasecmp(tmpbuffer, "Range: ", 7) == 0) { + workbuffer = tmpbuffer + 7; + + if (strncasecmp(workbuffer, "bytes=", 6) == 0) { + workbuffer += 6; + + range_start = strtoul(workbuffer, &workbuffer_next, 10); + + workbuffer = workbuffer_next; + + if (*workbuffer == '-') { + workbuffer++; + + if (*workbuffer != '\r' && *workbuffer != '\n') { + range_end = strtoul(workbuffer, &workbuffer_next, 10); + } + } + } + } + + if (memcmp(tmpbuffer, "\r\n", 2) == 0) { break; } } filed_log_msg("GOT_HEADERS FD=..."); @@ -282,11 +329,25 @@ /* We only handle the "GET" method */ if (strcasecmp(method, "get") != 0) { return(NULL); } - return(path); + /* Determine range */ + if (range_end != 0) { + if (range_end <= range_start) { + return(NULL); + } + + range_length = range_end - range_start; + } + + /* Fill up structure to return */ + buffer_st->path = path; + buffer_st->offset = range_start; + buffer_st->length = range_length; + + return(buffer_st); } /* Return an error page */ static void filed_error_page(FILE *fp, const char *date_current, int error_number) { char *error_string = "ERRORUnable to process request"; @@ -300,17 +361,18 @@ error_string ); } /* Handle a single request from a client */ -static void filed_handle_client(int fd) { - struct filed_fileinfo *fileinfo, fileinfo_b; +static void filed_handle_client(int fd, struct filed_http_request *request) { + struct filed_fileinfo *fileinfo; ssize_t sendfile_ret; size_t sendfile_len; off_t sendfile_offset; - char *path, path_b[1010]; + char *path; char *date_current, date_current_b[64]; + int http_code; FILE *fp; /* Determine current time */ date_current = filed_format_time(date_current_b, sizeof(date_current_b), time(NULL)); @@ -320,13 +382,15 @@ close(fd); return; } - path = filed_get_http_request(fp, path_b, sizeof(path_b)); + request = filed_get_http_request(fp, request); + + path = request->path; - filed_log_msg("PROCESS_REPLY_START FD=... PATH=..."); + filed_log_msg("PROCESS_REPLY_START FD=... PATH=... RANGE_START=... RANGE_LENGTH=..."); if (path == NULL) { filed_error_page(fp, date_current, 500); filed_log_msg("PROCESS_REPLY_COMPLETE FD=... ERROR=500"); @@ -334,43 +398,69 @@ fclose(fp); return; } - fileinfo = filed_open_file(path, &fileinfo_b); + http_code = -1; + + fileinfo = filed_open_file(path, &request->fileinfo); if (fileinfo == NULL) { filed_error_page(fp, date_current, 404); filed_log_msg("PROCESS_REPLY_COMPLETE FD=... ERROR=404"); } else { - fprintf(fp, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", - date_current, - fileinfo->lastmod, - (unsigned long long) fileinfo->len, - fileinfo->type - ); - fflush(fp); - - filed_log_msg("PROCESS_REPLY_COMPLETE FD=... STATUS=200"); - - filed_log_msg("SEND_START IFD=... OFD=... BYTES=..."); - - sendfile_offset = 0; - sendfile_len = fileinfo->len; - while (1) { - sendfile_ret = sendfile(fd, fileinfo->fd, &sendfile_offset, sendfile_len); - if (sendfile_ret <= 0) { - break; - } - - sendfile_len -= sendfile_ret; - if (sendfile_len == 0) { - break; - } - } - - filed_log_msg("SEND_COMPLETE STATUS=... IFD=... OFD=... BYTES=... BYTES_SENT=..."); + if (request->offset != 0 || request->length >= 0) { + if ((size_t) request->offset >= fileinfo->len) { + filed_log_msg("PROCESS_REPLY_COMPLETE FD=... ERROR=416"); + + filed_error_page(fp, date_current, 416); + } + + if (request->length < 0) { + request->length = fileinfo->len - request->offset; + } + + filed_log_msg_debug("Partial request, starting at: %lu and running for %lu bytes", (unsigned long) request->offset, (unsigned long) request->length); + + http_code = 216; + } else { + http_code = 200; + request->offset = 0; + request->length = fileinfo->len; + } + + if (http_code > 0) { + fprintf(fp, "HTTP/1.1 %i OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nContent-Range: %llu-%llu/%llu\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", + http_code, + date_current, + fileinfo->lastmod, + (unsigned long long) request->length, + (unsigned long long) request->offset, (unsigned long long) (request->offset + request->length - 1), (unsigned long long) fileinfo->len, + fileinfo->type + ); + fflush(fp); + + filed_log_msg("PROCESS_REPLY_COMPLETE FD=... STATUS=200"); + + filed_log_msg("SEND_START IFD=... OFD=... BYTES=..."); + + sendfile_offset = request->offset; + sendfile_len = request->length; + while (1) { + sendfile_ret = sendfile(fd, fileinfo->fd, &sendfile_offset, sendfile_len); + if (sendfile_ret <= 0) { + break; + } + + sendfile_len -= sendfile_ret; + if (sendfile_len == 0) { + break; + } + } + + filed_log_msg("SEND_COMPLETE STATUS=... IFD=... OFD=... BYTES=... BYTES_SENT=..."); + } close(fileinfo->fd); filed_log_msg("CLOSE_FILE FD=..."); } @@ -383,10 +473,11 @@ } /* Handle incoming connections */ static void *filed_worker_thread(void *arg_v) { struct filed_worker_thread_args *arg; + struct filed_http_request request; struct sockaddr_in6 addr; socklen_t addrlen; int failure_count = 0, max_failure_count = MAX_FAILURE_COUNT; int master_fd, fd; @@ -423,11 +514,11 @@ /* Reset failure count*/ failure_count = 0; /* Handle socket */ - filed_handle_client(fd); + filed_handle_client(fd, &request); } /* Report error */ filed_log_msg("THREAD_DIED ABNORMAL");