parent
f767b6cd5f
commit
0755e549ff
|
@ -62,9 +62,10 @@ static void conn_send(struct uh_connection *conn, const void *data, ssize_t len)
|
|||
ev_io_start(conni->srv->loop, &conni->iow);
|
||||
}
|
||||
|
||||
static void conn_send_file(struct uh_connection *conn, const char *path)
|
||||
static void conn_send_file(struct uh_connection *conn, const char *path, size_t offset, size_t len)
|
||||
{
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
size_t min = 8192;
|
||||
struct stat st;
|
||||
int fd;
|
||||
|
||||
|
@ -76,13 +77,24 @@ static void conn_send_file(struct uh_connection *conn, const char *path)
|
|||
|
||||
fstat(fd, &st);
|
||||
|
||||
/* If the file is not greater than 8K, then append it to the HTTP head, send once */
|
||||
st.st_size -= buffer_put_fd(&conni->wb, fd, 8192, NULL);
|
||||
if (st.st_size > 0) {
|
||||
conni->file.size = st.st_size;
|
||||
conni->file.fd = fd;
|
||||
} else {
|
||||
if (offset >= st.st_size) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
lseek(fd, offset, SEEK_SET);
|
||||
st.st_size -= offset;
|
||||
|
||||
if (len == 0 || len > st.st_size)
|
||||
len = st.st_size;
|
||||
|
||||
/* If the file is not greater than 8K, then append it to the HTTP head, send once */
|
||||
if (len <= min) {
|
||||
buffer_put_fd(&conni->wb, fd, len, NULL);
|
||||
close(fd);
|
||||
} else {
|
||||
conni->file.size = len;
|
||||
conni->file.fd = fd;
|
||||
}
|
||||
|
||||
ev_io_start(conni->srv->loop, &conni->iow);
|
||||
|
|
114
src/file.c
114
src/file.c
|
@ -34,6 +34,7 @@
|
|||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "uhttpd_internal.h"
|
||||
#include "mimetypes.h"
|
||||
|
@ -152,6 +153,102 @@ static void file_if_gzip(struct uh_connection *conn, const char *path, const cha
|
|||
conn->printf(conn, "Content-Encoding: gzip\r\n");
|
||||
}
|
||||
|
||||
static bool file_range(struct uh_connection *conn, size_t size, size_t *start, size_t *end, bool *ranged)
|
||||
{
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
const struct uh_str hdr = conn->get_header(conn, "Range");
|
||||
int content_length;
|
||||
const char *reason;
|
||||
const char *p, *e;
|
||||
char buf[32];
|
||||
int i;
|
||||
|
||||
*start = 0;
|
||||
*end = size - 1;
|
||||
|
||||
if (!hdr.p) {
|
||||
*ranged = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hdr.len < 8)
|
||||
goto err;
|
||||
|
||||
p = hdr.p;
|
||||
e = hdr.p + hdr.len;
|
||||
|
||||
if (strncmp(p, "bytes=", 6))
|
||||
goto err;
|
||||
|
||||
p += 6;
|
||||
i = 0;
|
||||
|
||||
while (p < e) {
|
||||
if (i >= sizeof(buf) - 1)
|
||||
goto err;
|
||||
|
||||
if (isdigit(*p)) {
|
||||
buf[i++] = *p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*p != '-')
|
||||
goto err;
|
||||
|
||||
p++;
|
||||
buf[i] = '\0';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
*start = strtoul(buf, NULL, 0);
|
||||
|
||||
i = 0;
|
||||
|
||||
while (p < e) {
|
||||
if (i >= (sizeof(buf) - 1) || !isdigit(*p))
|
||||
goto err;
|
||||
buf[i++] = *p++;
|
||||
}
|
||||
|
||||
buf[i] = '\0';
|
||||
*end = strtoul(buf, NULL, 0);
|
||||
|
||||
if (*start >= size)
|
||||
goto err;
|
||||
|
||||
if (*end == 0)
|
||||
*end = size - 1;
|
||||
|
||||
if (*end < *start)
|
||||
goto err;
|
||||
|
||||
if (*end > size - 1)
|
||||
*end = size - 1;
|
||||
|
||||
*ranged = true;
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
reason = http_status_str(HTTP_STATUS_RANGE_NOT_SATISFIABLE);
|
||||
content_length = strlen(reason);
|
||||
|
||||
conn->send_status_line(conn, HTTP_STATUS_RANGE_NOT_SATISFIABLE, "Content-Type: text/plain\r\nConnection: close\r\n");
|
||||
conn->printf(conn, "Content-Length: %d\r\n", content_length);
|
||||
conn->printf(conn, "Content-Range: bytes */%" PRIu64 "\r\n", size);
|
||||
|
||||
conn->send(conn, "\r\n", 2);
|
||||
|
||||
conn->send(conn, reason, content_length);
|
||||
|
||||
conni->flags |= CONN_F_SEND_AND_CLOSE;
|
||||
|
||||
conn->done(conn);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void serve_file(struct uh_connection *conn)
|
||||
{
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
|
@ -160,8 +257,10 @@ void serve_file(struct uh_connection *conn)
|
|||
const char *docroot = srv->docroot;
|
||||
const char *index_page = srv->index_page;
|
||||
static char fullpath[512];
|
||||
size_t start, end;
|
||||
const char *mime;
|
||||
struct stat st;
|
||||
bool ranged;
|
||||
|
||||
if (!docroot || !docroot[0])
|
||||
docroot = ".";
|
||||
|
@ -210,6 +309,9 @@ void serve_file(struct uh_connection *conn)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!file_range(conn, st.st_size, &start, &end, &ranged))
|
||||
return;
|
||||
|
||||
if (!file_if_modified_since(conn, &st) ||
|
||||
!file_if_range(conn, &st) ||
|
||||
!file_if_unmodified_since(conn, &st)) {
|
||||
|
@ -217,14 +319,21 @@ void serve_file(struct uh_connection *conn)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ranged)
|
||||
conn->send_status_line(conn, HTTP_STATUS_PARTIAL_CONTENT, NULL);
|
||||
else
|
||||
conn->send_status_line(conn, HTTP_STATUS_OK, NULL);
|
||||
|
||||
file_response_ok_hdrs(conn, &st);
|
||||
|
||||
mime = file_mime_lookup(fullpath);
|
||||
|
||||
conn->printf(conn, "Content-Type: %s\r\n", mime);
|
||||
conn->printf(conn, "Content-Length: %" PRIu64 "\r\n", st.st_size);
|
||||
conn->printf(conn, "Content-Length: %" PRIu64 "\r\n", end - start + 1);
|
||||
|
||||
if (ranged)
|
||||
conn->printf(conn, "Content-Range: bytes %" PRIu64 "-%" PRIu64 "/%" PRIu64 "\r\n", start, end, st.st_size);
|
||||
else
|
||||
file_if_gzip(conn, fullpath, mime);
|
||||
|
||||
conn->printf(conn, "\r\n");
|
||||
|
@ -232,6 +341,7 @@ void serve_file(struct uh_connection *conn)
|
|||
if (conn->get_method(conn) == HTTP_HEAD)
|
||||
return;
|
||||
|
||||
conn->send_file(conn, fullpath);
|
||||
conn->send_file(conn, fullpath, start, end - start + 1);
|
||||
|
||||
conn->done(conn);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ struct uh_connection {
|
|||
*/
|
||||
void (*done)(struct uh_connection *conn);
|
||||
void (*send)(struct uh_connection *conn, const void *data, ssize_t len);
|
||||
void (*send_file)(struct uh_connection *conn, const char *path);
|
||||
void (*send_file)(struct uh_connection *conn, const char *path, size_t offset, size_t len);
|
||||
void (*printf)(struct uh_connection *conn, const char *format, ...);
|
||||
void (*vprintf)(struct uh_connection *conn, const char *format, va_list arg);
|
||||
void (*send_status_line)(struct uh_connection *conn, int code, const char *extra_headers);
|
||||
|
|
Loading…
Reference in New Issue