diff --git a/src/client.c b/src/client.c index 6f92de4..c85cdd2 100644 --- a/src/client.c +++ b/src/client.c @@ -198,7 +198,50 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int goto respond; } - // TODO Ranges + char *range = http_get_header_field(&req.hdr, "Range"); + if (range != NULL) { + if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) { + res.status = http_get_status(416); + http_remove_header_field(&res.hdr, "Content-Type", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "Last-Modified", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "ETag", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "Cache-Control", HTTP_REMOVE_ALL); + goto respond; + } + range += 6; + char *ptr = strchr(range, '-'); + if (ptr == NULL) { + res.status = http_get_status(416); + goto respond; + } + file = fopen(uri.filename, "rb"); + fseek(file, 0, SEEK_END); + unsigned long file_len = ftell(file); + fseek(file, 0, SEEK_SET); + if (file_len == 0) { + content_length = 0; + goto respond; + } + long num1 = 0; + long num2 = (long) file_len - 1; + + if (ptr != range) num1 = (long) strtoul(range, NULL, 10); + if (ptr[1] != 0) num2 = (long) strtoul(ptr + 1, NULL, 10); + + if (num1 >= file_len || num2 >= file_len || num1 > num2) { + res.status = http_get_status(416); + goto respond; + } + sprintf(buf0, "bytes %li-%li/%li", num1, num2, file_len); + http_add_header_field(&res.hdr, "Content-Range", buf0); + + res.status = http_get_status(206); + fseek(file, num1, SEEK_SET); + content_length = num2 - num1 + 1; + + goto respond; + } + char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding"); if (uri.meta->filename_comp[0] != 0 && accept_encoding != NULL && strstr(accept_encoding, "deflate") != NULL) { file = fopen(uri.meta->filename_comp, "rb"); @@ -340,6 +383,9 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } else if (file != NULL) { while (snd_len < content_length) { len = fread(&buffer, 1, CHUNK_SIZE, file); + if (snd_len + len > content_length) { + len = content_length - snd_len; + } if (client->enc) { ret = SSL_write(client->ssl, buffer, (int) len); if (ret <= 0) { diff --git a/src/http.c b/src/http.c index 6d14ef7..20f2890 100644 --- a/src/http.c +++ b/src/http.c @@ -45,11 +45,11 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) char *pos1 = memchr(buf, ':', end_ptr - buf); char *pos2; if (pos1 == NULL) { - print(ERR_STR "Unable to parse header: Invalid version" CLR_STR); + print(ERR_STR "Unable to parse header" CLR_STR); return 3; } - unsigned long len = pos1 - buf; + long len = pos1 - buf; hdr->fields[hdr->field_num][0] = malloc(len + 1); sprintf(hdr->fields[hdr->field_num][0], "%.*s", (int) len, buf); http_to_camel_case(hdr->fields[hdr->field_num][0], HTTP_CAMEL); @@ -59,10 +59,15 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) while (pos1[0] == ' ') pos1++; while (pos2[0] == ' ') pos2--; len = pos2 - pos1 + 1; - hdr->fields[hdr->field_num][1] = malloc(len + 1); - sprintf(hdr->fields[hdr->field_num][1], "%.*s", (int) len, pos1); - hdr->field_num++; + if (len <= 0) { + hdr->fields[hdr->field_num][1] = malloc(1); + hdr->fields[hdr->field_num][1][0] = 0; + } else { + hdr->fields[hdr->field_num][1] = malloc(len + 1); + sprintf(hdr->fields[hdr->field_num][1], "%.*s", (int) len, pos1); + } + hdr->field_num++; return 0; } @@ -247,7 +252,7 @@ http_status *http_get_status(unsigned short status_code) { } http_error_msg *http_get_error_msg(unsigned short status_code) { - for (int i = 0; i < sizeof(http_error_messages) / sizeof(http_get_error_msg); i++) { + for (int i = 0; i < sizeof(http_error_messages) / sizeof(http_error_msg); i++) { if (http_error_messages[i].code == status_code) { return &http_error_messages[i]; } diff --git a/src/http.h b/src/http.h index 030a7e4..337a549 100644 --- a/src/http.h +++ b/src/http.h @@ -81,7 +81,7 @@ http_status http_statuses[] = { {413, "Client Error", "Request Entity Too Large"}, {414, "Client Error", "Request-URI Too Long"}, {415, "Client Error", "Unsupported Media Type"}, - {416, "Client Error", "Requested Range Not Satisfiable"}, + {416, "Client Error", "Range Not Satisfiable"}, {417, "Client Error", "Expectation Failed"}, {500, "Server Error", "Internal Server Error"}, @@ -108,6 +108,7 @@ http_error_msg http_error_messages[] = { {413, "The server is refusing to process a request because the request entity is larger than the server is willing or able to process."}, {414, "The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret."}, {415, "The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method."}, + {416, "None of the ranges in the request's Range header field overlap the current extent of the selected resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive request of small or overlapping ranges."}, {417, "The expectation given in an Expect request-header field could not be met by this server, or, if the server is a proxy, the server has unambiguous evidence that the request could not be met by the next-hop server."}, {500, "The server encountered an unexpected condition which prevented it from fulfilling the request."},