ostree/trivial-httpd: Add Last-Modified/ETag support

This is basic support for the
Last-Modified/ETag/If-Modified-Since/If-None-Match headers. It’s not
high performance, and doesn’t support all of the related caching
features (like the If-Match header, etc.).

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This commit is contained in:
Philip Withnall 2020-10-09 18:46:06 +01:00
parent a522bf7628
commit 92215025aa
1 changed files with 80 additions and 18 deletions

View File

@ -206,6 +206,15 @@ close_socket (SoupMessage *msg, gpointer user_data)
#endif #endif
} }
/* Returns the ETag including the surrounding quotes */
static gchar *
calculate_etag (GMappedFile *mapping)
{
g_autoptr(GBytes) bytes = g_mapped_file_get_bytes (mapping);
g_autofree gchar *checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, bytes);
return g_strconcat ("\"", checksum, "\"", NULL);
}
static void static void
do_get (OtTrivialHttpd *self, do_get (OtTrivialHttpd *self,
SoupServer *server, SoupServer *server,
@ -368,30 +377,40 @@ do_get (OtTrivialHttpd *self,
goto out; goto out;
} }
glnx_autofd int fd = openat (self->root_dfd, path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
goto out;
}
g_autoptr(GMappedFile) mapping = g_mapped_file_new_from_fd (fd, FALSE, NULL);
if (!mapping)
{
soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
goto out;
}
(void) close (fd); fd = -1;
/* Send caching headers */
g_autoptr(GDateTime) last_modified = g_date_time_new_from_unix_utc (stbuf.st_mtim.tv_sec);
if (last_modified != NULL)
{
g_autofree gchar *formatted = g_date_time_format (last_modified, "%a, %d %b %Y %H:%M:%S GMT");
soup_message_headers_append (msg->response_headers, "Last-Modified", formatted);
}
g_autofree gchar *etag = calculate_etag (mapping);
if (etag != NULL)
soup_message_headers_append (msg->response_headers, "ETag", etag);
if (msg->method == SOUP_METHOD_GET) if (msg->method == SOUP_METHOD_GET)
{ {
glnx_autofd int fd = -1;
g_autoptr(GMappedFile) mapping = NULL;
gsize buffer_length, file_size; gsize buffer_length, file_size;
SoupRange *ranges; SoupRange *ranges;
int ranges_length; int ranges_length;
gboolean have_ranges; gboolean have_ranges;
fd = openat (self->root_dfd, path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
goto out;
}
mapping = g_mapped_file_new_from_fd (fd, FALSE, NULL);
if (!mapping)
{
soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
goto out;
}
(void) close (fd); fd = -1;
file_size = g_mapped_file_get_length (mapping); file_size = g_mapped_file_get_length (mapping);
have_ranges = soup_message_headers_get_ranges(msg->request_headers, file_size, &ranges, &ranges_length); have_ranges = soup_message_headers_get_ranges(msg->request_headers, file_size, &ranges, &ranges_length);
if (opt_force_ranges && !have_ranges && g_strrstr (path, "/objects") != NULL) if (opt_force_ranges && !have_ranges && g_strrstr (path, "/objects") != NULL)
@ -447,7 +466,50 @@ do_get (OtTrivialHttpd *self,
soup_message_headers_append (msg->response_headers, soup_message_headers_append (msg->response_headers,
"Content-Length", length); "Content-Length", length);
} }
soup_message_set_status (msg, SOUP_STATUS_OK);
/* Check clients caching headers. */
const gchar *if_modified_since = soup_message_headers_get_one (msg->request_headers,
"If-Modified-Since");
const gchar *if_none_match = soup_message_headers_get_one (msg->request_headers,
"If-None-Match");
if (if_none_match != NULL && etag != NULL)
{
if (g_strcmp0 (etag, if_none_match) == 0)
{
soup_message_set_status (msg, SOUP_STATUS_NOT_MODIFIED);
soup_message_body_truncate (msg->response_body);
}
else
{
soup_message_set_status (msg, SOUP_STATUS_OK);
}
}
else if (if_modified_since != NULL && last_modified != NULL)
{
SoupDate *if_modified_since_sd = soup_date_new_from_string (if_modified_since);
g_autoptr(GDateTime) if_modified_since_dt = NULL;
if (if_modified_since_sd != NULL)
if_modified_since_dt = g_date_time_new_from_unix_utc (soup_date_to_time_t (if_modified_since_sd));
if (if_modified_since_dt != NULL &&
g_date_time_compare (last_modified, if_modified_since_dt) <= 0)
{
soup_message_set_status (msg, SOUP_STATUS_NOT_MODIFIED);
soup_message_body_truncate (msg->response_body);
}
else
{
soup_message_set_status (msg, SOUP_STATUS_OK);
}
g_clear_pointer (&if_modified_since_sd, soup_date_free);
}
else
{
soup_message_set_status (msg, SOUP_STATUS_OK);
}
} }
out: out:
{ {