Added support for ranged requests

This commit is contained in:
Thomas Kolb 2013-01-20 23:57:20 +01:00
parent cb8c926296
commit bd21ab6dd7

103
main.c
View file

@ -31,6 +31,11 @@ struct ConnectionState {
struct stat targetStat; struct stat targetStat;
}; };
struct RequestRange {
off_t start;
off_t length;
};
char *shareRoot; char *shareRoot;
struct MHD_Response *error403Response; struct MHD_Response *error403Response;
struct MHD_Response *error404Response; struct MHD_Response *error404Response;
@ -54,12 +59,97 @@ void request_completed(void *cls,
free(connstate); free(connstate);
} }
int parse_range(const char *range, off_t total_len, struct RequestRange *result) {
char *numstr;
char *dashptr;
int dashpos;
off_t num;
LOG(LVL_DEBUG, "Requested Range is %s", range);
if(range == NULL) {
result->start = 0;
result->length = total_len;
return 0;
}
numstr = strchr(range, '=');
if(numstr == NULL) {
return -1;
}
numstr += 1;
LOG(LVL_DUMP, "Numeric part of range: %s", numstr);
dashptr = strchr(numstr, '-');
if(dashptr == NULL) {
// no '-' found
return -1;
}
dashpos = dashptr - numstr;
LOG(LVL_DUMP, "Dash found at position: %i", dashpos);
if(dashpos == 0) {
// example: bytes=-500
// -> letzte 500 bytes ausgeben
if(sscanf(dashptr+1, "%li", &num) != 1) {
return -1;
}
// total = 1000, num = 100 -> start=500, length=500
result->start = total_len - num - 1;
result->length = num;
} else {
// examples: bytes=100-200; bytes=300-
// parse the first number
if(sscanf(numstr, "%li", &num) != 1) {
return -1;
}
result->start = num;
if(numstr[dashpos + 1] == '\0') {
// everything from num to the end
result->length = total_len - num;
return 0;
}
// a complete range was given -> parse the second number
if(sscanf(numstr+dashpos+1, "%li", &num) != 1) {
return -1;
}
if(num < result->start) {
return -1;
}
result->length = num - result->start + 1;
}
return 0;
}
int serv_regular_file(struct MHD_Connection *connection, struct ConnectionState *connstate) { int serv_regular_file(struct MHD_Connection *connection, struct ConnectionState *connstate) {
struct MHD_Response *response; struct MHD_Response *response;
struct RequestRange range;
const char *rangestr;
char buf[256];
int ret; int ret;
LOG(LVL_DEBUG, "Serving %s as a regular file.", connstate->localFileName); LOG(LVL_DEBUG, "Serving %s as a regular file.", connstate->localFileName);
rangestr = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range");
if(0 != parse_range(rangestr, connstate->targetStat.st_size, &range)) {
LOG(LVL_ERR, "Cannot parse range header.");
return MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, error500Response);
}
LOG(LVL_DUMP, "Requested range: start=%lu, length=%lu", range.start, range.length);
// open a file descriptor to the file // open a file descriptor to the file
int fd = open(connstate->localFileName, O_RDONLY); int fd = open(connstate->localFileName, O_RDONLY);
@ -71,9 +161,16 @@ int serv_regular_file(struct MHD_Connection *connection, struct ConnectionState
} }
// no error so far -> serve the file // no error so far -> serve the file
response = MHD_create_response_from_fd( response = MHD_create_response_from_fd_at_offset(
connstate->targetStat.st_size, range.length,
fd); fd,
range.start);
// build content range header
sprintf(buf, "bytes %li-%li/%li", range.start, range.start+range.length-1, connstate->targetStat.st_size);
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_RANGE, buf);
LOG(LVL_DUMP, "Content-Range: %s", buf);
#ifdef HAVE_MAGIC #ifdef HAVE_MAGIC
// if libmagic is available, determine the correct MIME type for the file // if libmagic is available, determine the correct MIME type for the file