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);
|
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;
|
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||||
|
size_t min = 8192;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
@ -76,13 +77,24 @@ static void conn_send_file(struct uh_connection *conn, const char *path)
|
||||||
|
|
||||||
fstat(fd, &st);
|
fstat(fd, &st);
|
||||||
|
|
||||||
/* If the file is not greater than 8K, then append it to the HTTP head, send once */
|
if (offset >= st.st_size) {
|
||||||
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 {
|
|
||||||
close(fd);
|
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);
|
ev_io_start(conni->srv->loop, &conni->iow);
|
||||||
|
|
118
src/file.c
118
src/file.c
|
@ -34,6 +34,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "uhttpd_internal.h"
|
#include "uhttpd_internal.h"
|
||||||
#include "mimetypes.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");
|
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)
|
void serve_file(struct uh_connection *conn)
|
||||||
{
|
{
|
||||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)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 *docroot = srv->docroot;
|
||||||
const char *index_page = srv->index_page;
|
const char *index_page = srv->index_page;
|
||||||
static char fullpath[512];
|
static char fullpath[512];
|
||||||
|
size_t start, end;
|
||||||
const char *mime;
|
const char *mime;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
bool ranged;
|
||||||
|
|
||||||
if (!docroot || !docroot[0])
|
if (!docroot || !docroot[0])
|
||||||
docroot = ".";
|
docroot = ".";
|
||||||
|
@ -210,6 +309,9 @@ void serve_file(struct uh_connection *conn)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!file_range(conn, st.st_size, &start, &end, &ranged))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!file_if_modified_since(conn, &st) ||
|
if (!file_if_modified_since(conn, &st) ||
|
||||||
!file_if_range(conn, &st) ||
|
!file_if_range(conn, &st) ||
|
||||||
!file_if_unmodified_since(conn, &st)) {
|
!file_if_unmodified_since(conn, &st)) {
|
||||||
|
@ -217,21 +319,29 @@ void serve_file(struct uh_connection *conn)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->send_status_line(conn, HTTP_STATUS_OK, NULL);
|
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);
|
file_response_ok_hdrs(conn, &st);
|
||||||
|
|
||||||
mime = file_mime_lookup(fullpath);
|
mime = file_mime_lookup(fullpath);
|
||||||
|
|
||||||
conn->printf(conn, "Content-Type: %s\r\n", mime);
|
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);
|
||||||
|
|
||||||
file_if_gzip(conn, fullpath, mime);
|
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");
|
conn->printf(conn, "\r\n");
|
||||||
|
|
||||||
if (conn->get_method(conn) == HTTP_HEAD)
|
if (conn->get_method(conn) == HTTP_HEAD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
conn->send_file(conn, fullpath);
|
conn->send_file(conn, fullpath, start, end - start + 1);
|
||||||
|
|
||||||
conn->done(conn);
|
conn->done(conn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ struct uh_connection {
|
||||||
*/
|
*/
|
||||||
void (*done)(struct uh_connection *conn);
|
void (*done)(struct uh_connection *conn);
|
||||||
void (*send)(struct uh_connection *conn, const void *data, ssize_t len);
|
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 (*printf)(struct uh_connection *conn, const char *format, ...);
|
||||||
void (*vprintf)(struct uh_connection *conn, const char *format, va_list arg);
|
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);
|
void (*send_status_line)(struct uh_connection *conn, int code, const char *extra_headers);
|
||||||
|
|
Loading…
Reference in New Issue