summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVivien Kraus <vivien@planete-kraus.eu>2023-03-11 11:49:22 +0100
committerVivien Kraus <vivien@planete-kraus.eu>2023-03-11 14:38:56 +0100
commit419d7ec40b30e70d0f0f00ad96909c6978c9eace (patch)
treeb4ea7e89b2a18e4e602f757b11b7f5df516e3426
parentdbaf5022d8b24fdd1672dd00eacb7532fbdc5310 (diff)
Read cache entries from a file
For now, only the request and response dates are read, and whether the response has been invalidated (e.g. due to a POST to the same resource).
-rw-r--r--bootstrap.conf39
-rw-r--r--include/disfluid.h90
-rw-r--r--src/adwaita/Makefile.am5
-rw-r--r--src/libdisfluid/Makefile.am3
-rw-r--r--src/libdisfluid/disfluid-cache-entry.h504
-rw-r--r--src/libdisfluid/disfluid-tests.h283
-rw-r--r--src/libdisfluid/main.c100
7 files changed, 1012 insertions, 12 deletions
diff --git a/bootstrap.conf b/bootstrap.conf
index a80b876..4b8a2e6 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -18,13 +18,40 @@
# gnulib modules used by this package.
gnulib_modules="
-gettext-h valgrind-tests git-version-gen
-gitlog-to-changelog vc-list-files
-lib-msvc-compat lib-symbol-visibility
-relocatable-lib-lgpl locale setlocale localeconv
-useless-if-before-free do-release-commit-and-tag
-announce-gen stdbool attribute lseek remove stdio
+announce-gen
+assert
+assert-h
+attribute
+close
+do-release-commit-and-tag
+errno
+fclose
+free-posix
+gettext-h
+git-version-gen
+gitlog-to-changelog
+lib-msvc-compat
+lib-symbol-visibility
+locale setlocale localeconv
+lseek
+malloc-gnu
+mkstemp
+open
+read
+realloc-gnu
+relocatable-lib-lgpl
+remove
+stdbool
+stdio
+strdup
+sys_stat
+time
+tmpfile
+useless-if-before-free
+valgrind-tests
vasprintf
+vc-list-files
+write
"
COPYRIGHT_HOLDER='Vivien Kraus'
diff --git a/include/disfluid.h b/include/disfluid.h
index 3842571..63fa1fc 100644
--- a/include/disfluid.h
+++ b/include/disfluid.h
@@ -2,6 +2,8 @@
# define H_DISFLUID_INCLUDED
# include <stdlib.h>
+# include <stdio.h>
+# include <time.h>
# if defined _WIN32 && !defined __CYGWIN__
# define LIBDISFLUID_DLL_MADNESS 1
@@ -40,6 +42,39 @@
# define LIBDISFLUID_CONST
# endif
+# ifdef ATTRIBUTE_FD_ARG
+# define LIBDISFLUID_FD_ARG ATTRIBUTE_FD_ARG
+# else
+# define LIBDISFLUID_FD_ARG()
+# endif
+
+# define LIBDISFLUID_FD_ARG_1 \
+ LIBDISFLUID_FD_ARG (1)
+
+# ifdef ATTRIBUTE_FD_ARG_READ
+# define LIBDISFLUID_FD_ARG_READ ATTRIBUTE_FD_ARG_READ
+# else
+# define LIBDISFLUID_FD_ARG_READ(i)
+# endif
+
+# define LIBDISFLUID_FD_ARG_READ_1 \
+ LIBDISFLUID_FD_ARG_READ (1)
+
+# ifdef ATTRIBUTE_FD_ARG_WRITE
+# define LIBDISFLUID_FD_ARG_WRITE ATTRIBUTE_FD_ARG_WRITE
+# else
+# define LIBDISFLUID_FD_ARG_WRITE(i)
+# endif
+
+# define LIBDISFLUID_FD_ARG_WRITE_1 \
+ LIBDISFLUID_FD_ARG_WRITE (1)
+
+# define LIBDISFLUID_FD_ARG_WRITE_2 \
+ LIBDISFLUID_FD_ARG_WRITE (2)
+
+# define LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY \
+ LIBDISFLUID_DEALLOC_WITH (disfluid_cache_entry_free, 1)
+
# ifdef __cplusplus
extern "C"
{
@@ -91,6 +126,61 @@ extern "C"
LIBDISFLUID_CONST LIBDISFLUID_API const char
*disfluid_translation_credits (void);
+ struct disfluid_cache_entry;
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_free (struct disfluid_cache_entry *entry);
+
+ LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY
+ extern struct disfluid_cache_entry *disfluid_cache_entry_alloc (void);
+
+ LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY
+ extern struct disfluid_cache_entry
+ *disfluid_cache_entry_from_file_name (const char *filename);
+
+ LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY
+ LIBDISFLUID_FD_ARG_READ_1 extern struct disfluid_cache_entry
+ *disfluid_cache_entry_from_fd (int fd);
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_set_request_date (struct disfluid_cache_entry *entry,
+ const struct timespec
+ *request_date);
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_set_response_date (struct disfluid_cache_entry
+ *entry,
+ const struct timespec
+ *request_date);
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_invalidate (struct disfluid_cache_entry *entry);
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_set_invalidated (struct disfluid_cache_entry *entry,
+ int invalidated);
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_get_request_date (const struct disfluid_cache_entry
+ *entry, struct timespec *date);
+
+ LIBDISFLUID_API extern void
+ disfluid_cache_entry_get_response_date (const struct disfluid_cache_entry
+ *entry, struct timespec *date);
+
+ LIBDISFLUID_API extern int
+ disfluid_cache_entry_invalidated (const struct disfluid_cache_entry
+ *entry);
+
+ LIBDISFLUID_API extern int
+ disfluid_cache_entry_save_other_file_name (const struct
+ disfluid_cache_entry *entry,
+ const char *filename);
+
+ LIBDISFLUID_API LIBDISFLUID_FD_ARG_WRITE_2 extern int
+ disfluid_cache_entry_save_other_fd (const struct disfluid_cache_entry
+ *entry, int fd);
+
# ifdef __cplusplus
}
# endif /* __cplusplus */
diff --git a/src/adwaita/Makefile.am b/src/adwaita/Makefile.am
index 80d724a..5b30d64 100644
--- a/src/adwaita/Makefile.am
+++ b/src/adwaita/Makefile.am
@@ -13,9 +13,12 @@ noinst_LTLIBRARIES += %D%/libdisfluid-adwaita.la
-DINSTALLDIR=\"$(bindir)\"
%C%_libdisfluid_adwaita_la_LIBADD = \
- lib/libgnu.la
+ lib/libgnu.la \
+ src/libdisfluid/libdisfluid.la
%C%_libdisfluid_adwaita_la_LDFLAGS = \
+ $(CLOCK_TIME_LIB) \
+ $(GETRANDOM_LIB) \
$(LIBTHREAD) \
$(LTLIBINTL) \
$(SETLOCALE_LIB) \
diff --git a/src/libdisfluid/Makefile.am b/src/libdisfluid/Makefile.am
index 97c2ba3..002074d 100644
--- a/src/libdisfluid/Makefile.am
+++ b/src/libdisfluid/Makefile.am
@@ -2,6 +2,7 @@ lib_LTLIBRARIES += %D%/libdisfluid.la
%C%_libdisfluid_la_SOURCES = \
%D%/disfluid-authors.h \
+ %D%/disfluid-cache-entry.h \
%D%/disfluid-init.h \
%D%/disfluid-tests.h \
%D%/disfluid-version.h \
@@ -22,6 +23,8 @@ lib_LTLIBRARIES += %D%/libdisfluid.la
%C%_libdisfluid_la_LDFLAGS = \
-no-undefined \
-version-info $(LTV_CURRENT):$(LTV_REVISION):$(LTV_AGE) \
+ $(CLOCK_TIME_LIB) \
+ $(GETRANDOM_LIB) \
$(LIBTHREAD) \
$(LTLIBINTL) \
$(SETLOCALE_LIB) \
diff --git a/src/libdisfluid/disfluid-cache-entry.h b/src/libdisfluid/disfluid-cache-entry.h
new file mode 100644
index 0000000..c0ddaf2
--- /dev/null
+++ b/src/libdisfluid/disfluid-cache-entry.h
@@ -0,0 +1,504 @@
+#ifndef DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED
+# define DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED
+
+MAYBE_UNUSED static struct disfluid_cache_entry *cache_entry_alloc (void);
+
+MAYBE_UNUSED static void
+cache_entry_free (struct disfluid_cache_entry *entry);
+
+MAYBE_UNUSED static struct disfluid_cache_entry
+ *cache_entry_from_file_name (const char *file_name);
+
+MAYBE_UNUSED static struct disfluid_cache_entry *cache_entry_from_fd (int fd);
+
+MAYBE_UNUSED static int
+cache_entry_save_other_file_name (const struct disfluid_cache_entry *entry,
+ const char *file_name);
+
+MAYBE_UNUSED static int
+cache_entry_save_other_fd (const struct disfluid_cache_entry *entry, int fd);
+
+MAYBE_UNUSED static void
+cache_entry_set_request_date (struct disfluid_cache_entry *entry,
+ const struct timespec *date);
+
+MAYBE_UNUSED static void
+cache_entry_set_response_date (struct disfluid_cache_entry *entry,
+ const struct timespec *date);
+
+MAYBE_UNUSED static void
+cache_entry_invalidate (struct disfluid_cache_entry *entry);
+
+MAYBE_UNUSED static void
+cache_entry_set_invalidated (struct disfluid_cache_entry *entry,
+ bool invalidated);
+
+MAYBE_UNUSED static void
+cache_entry_get_request_date (const struct disfluid_cache_entry *entry,
+ struct timespec *date);
+
+MAYBE_UNUSED static void
+cache_entry_get_response_date (const struct disfluid_cache_entry *entry,
+ struct timespec *date);
+
+MAYBE_UNUSED static bool
+cache_entry_invalidated (const struct disfluid_cache_entry *entry);
+
+# include <assert.h>
+
+# include "disfluid-init.h"
+
+struct disfluid_cache_entry
+{
+ int fd;
+ off_t start;
+ off_t header_stop;
+ struct timespec request_date;
+ struct timespec response_date;
+ bool invalidated;
+};
+
+static struct disfluid_cache_entry *
+cache_entry_alloc (void)
+{
+ ensure_init ();
+ struct disfluid_cache_entry *ret =
+ malloc (sizeof (struct disfluid_cache_entry));
+ if (ret == NULL)
+ {
+ abort ();
+ }
+ ret->fd = -1;
+ ret->start = 0;
+ ret->header_stop = 0;
+ ret->request_date.tv_sec = 0;
+ ret->request_date.tv_nsec = 0;
+ ret->response_date.tv_sec = 0;
+ ret->response_date.tv_nsec = 0;
+ ret->invalidated = false;
+ return ret;
+}
+
+static void
+cache_entry_free (struct disfluid_cache_entry *entry)
+{
+ if (entry != NULL)
+ {
+ if (entry->fd >= 0)
+ {
+ close (entry->fd);
+ }
+ }
+ free (entry);
+}
+
+static struct disfluid_cache_entry *
+cache_entry_from_file_name (const char *file_name)
+{
+ int fd = open (file_name, O_RDONLY, S_IRUSR);
+ if (fd < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *ret = cache_entry_from_fd (fd);
+ if (ret == NULL)
+ {
+ abort ();
+ }
+ return ret;
+}
+
+static inline void
+cache_entry_string_append (char **str, size_t *max, size_t *n,
+ size_t suffix_length, const char *suffix)
+{
+ if (*n + suffix_length > *max)
+ {
+ *str = realloc (*str, 2 * *max);
+ if (*str == NULL)
+ {
+ abort ();
+ }
+ *max *= 2;
+ cache_entry_string_append (str, max, n, suffix_length, suffix);
+ }
+ else
+ {
+ memcpy (&((*str)[*n]), suffix, suffix_length);
+ *n += suffix_length;
+ }
+}
+
+static inline ssize_t
+cache_entry_read_next_line_aux (int fd, size_t *restrict buffer_max,
+ size_t *restrict buffer_length, char **buffer,
+ size_t checked)
+{
+ while (checked + 1 < *buffer_length
+ && ((*buffer)[checked] != '\r' || (*buffer)[checked + 1] != '\n'))
+ {
+ checked++;
+ }
+ if (checked + 1 < *buffer_length)
+ {
+ return checked + 2;
+ }
+ if (*buffer_length == *buffer_max)
+ {
+ *buffer = realloc (*buffer, 2 * *buffer_max);
+ if (*buffer == NULL)
+ {
+ abort ();
+ }
+ *buffer_max *= 2;
+ }
+ assert (*buffer_max > *buffer_length);
+ ssize_t n_added =
+ read (fd, &((*buffer)[*buffer_length]), *buffer_max - *buffer_length);
+ if (n_added < 0)
+ {
+ abort ();
+ }
+ else if (n_added == 0)
+ {
+ return *buffer_length;
+ }
+ else
+ {
+ assert (*buffer_length + n_added <= *buffer_max);
+ *buffer_length += n_added;
+ }
+ return cache_entry_read_next_line_aux (fd, buffer_max, buffer_length,
+ buffer, checked);
+}
+
+static inline void
+cache_entry_read_next_line (int fd, size_t *restrict buffer_max,
+ size_t *restrict buffer_length, char **buffer,
+ char **line, size_t *line_length)
+{
+ ssize_t length =
+ cache_entry_read_next_line_aux (fd, buffer_max, buffer_length, buffer, 0);
+ if (length < 0)
+ {
+ abort ();
+ }
+ *line = malloc (length + 1);
+ if (*line == NULL)
+ {
+ abort ();
+ }
+ *line_length = length;
+ memcpy (*line, *buffer, length);
+ (*line)[length] = '\0';
+ memmove (*buffer, &((*buffer)[length]), *buffer_length - length);
+ *buffer_length -= length;
+}
+
+static inline void
+cache_entry_enumerate_header_lines (const struct disfluid_cache_entry *entry,
+ void (*cb) (void *, const char *,
+ const char *), void *context)
+{
+ off_t start_offset = lseek (entry->fd, 0, SEEK_CUR);
+ if (start_offset < 0)
+ {
+ abort ();
+ }
+ size_t buffer_max = 1;
+ size_t buffer_length = 0;
+ char *buffer = malloc (buffer_max);
+ if (buffer == NULL)
+ {
+ abort ();
+ }
+ char *line;
+ size_t line_length;
+ while (true)
+ {
+ cache_entry_read_next_line (entry->fd, &buffer_max, &buffer_length,
+ &buffer, &line, &line_length);
+ if (line == NULL)
+ {
+ /* Memory error */
+ abort ();
+ }
+ if (STREQ (line, "\r\n"))
+ {
+ /* End of parse */
+ ssize_t undo = buffer_length;
+ lseek (entry->fd, -undo, SEEK_CUR);
+ free (buffer);
+ free (line);
+ return;
+ }
+ char *header_name = malloc (line_length + 1);
+ if (header_name == NULL)
+ {
+ abort ();
+ }
+ size_t header_name_length = 0;
+ for (header_name_length = 0;
+ header_name_length < line_length
+ && line[header_name_length] != ':'; header_name_length++)
+ {
+ if (line[header_name_length] >= 'A'
+ && line[header_name_length] <= 'Z')
+ {
+ line[header_name_length] =
+ (line[header_name_length] - 'A') + 'a';
+ }
+ header_name[header_name_length] = line[header_name_length];
+ }
+ header_name[header_name_length] = '\0';
+ if (line[header_name_length] != ':')
+ {
+ /* HTTP bad header, restore position */
+ lseek (entry->fd, start_offset, SEEK_SET);
+ free (buffer);
+ free (line);
+ free (header_name);
+ return;
+ }
+ header_name_length++;
+ cb (context, header_name, &(line[header_name_length]));
+ free (line);
+ free (header_name);
+ }
+}
+
+static inline void
+cache_entry_parse_timespec (const char *data, struct timespec *ts)
+{
+ /* A timespec is represented as SSS.NNN where SSS is the number of seconds, and NNN the number of nanoseconds. */
+ while (*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n')
+ {
+ data++;
+ }
+ struct timespec r = {.tv_sec = 0,.tv_nsec = 0 };
+ size_t n_seconds_digits = 0;
+ for (n_seconds_digits = 0;
+ data[n_seconds_digits] >= '0' && data[n_seconds_digits] <= '9';
+ n_seconds_digits++)
+ {
+ r.tv_sec *= 10;
+ r.tv_sec += (data[n_seconds_digits] - '0');
+ }
+ data += n_seconds_digits;
+ const bool has_dot = (data[0] == '.');
+ if (has_dot)
+ {
+ data++;
+ }
+ int nano_digits[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ for (size_t i = 0; i < 9 && data[i] >= '0' && data[i] <= '9'; i++)
+ {
+ nano_digits[i] = (data[i] - '0');
+ }
+ for (size_t i = 0; i < 9; i++)
+ {
+ r.tv_nsec *= 10;
+ r.tv_nsec += nano_digits[i];
+ }
+ ts->tv_sec = r.tv_sec;
+ ts->tv_nsec = r.tv_nsec;
+}
+
+static inline void
+cache_entry_iterate_metadata (void *ctx, const char *header,
+ const char *value)
+{
+ struct disfluid_cache_entry *entry = ctx;
+ if (STREQ (header, "request-date"))
+ {
+ cache_entry_parse_timespec (value, &(entry->request_date));
+ }
+ else if (STREQ (header, "response-date"))
+ {
+ cache_entry_parse_timespec (value, &(entry->response_date));
+ }
+ else if (STREQ (header, "invalidated"))
+ {
+ while (*value == ' ' || *value == '\t' || *value == '\r'
+ || *value == '\n')
+ {
+ value++;
+ }
+ if (STREQ (value, "true\r\n"))
+ {
+ entry->invalidated = true;
+ }
+ else if (STREQ (value, "false\r\n"))
+ {
+ entry->invalidated = false;
+ }
+ }
+}
+
+static inline void
+cache_entry_scan (struct disfluid_cache_entry *entry)
+{
+ entry->start = lseek (entry->fd, 0, SEEK_CUR);
+ cache_entry_enumerate_header_lines (entry, cache_entry_iterate_metadata,
+ entry);
+ entry->header_stop = lseek (entry->fd, 0, SEEK_CUR);
+ /* Reset the file descriptor position, in case it is shared */
+ lseek (entry->fd, entry->start, SEEK_SET);
+}
+
+static struct disfluid_cache_entry *
+cache_entry_from_fd (int fd)
+{
+ struct disfluid_cache_entry *ret = cache_entry_alloc ();
+ if (ret == NULL)
+ {
+ abort ();
+ }
+ ret->fd = dup (fd);
+ if (ret->fd < 0)
+ {
+ abort ();
+ }
+ cache_entry_scan (ret);
+ assert (ret->start >= 0);
+ assert (ret->header_stop >= 0);
+ if (ret->header_stop == ret->start)
+ {
+ /* Assume that reading failed. */
+ cache_entry_free (ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+static int
+cache_entry_save_other_file_name (const struct disfluid_cache_entry *entry,
+ const char *file_name)
+{
+ int fd = open (file_name, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+ if (fd < 0)
+ {
+ return errno;
+ }
+ return cache_entry_save_other_fd (entry, fd);
+}
+
+static int
+cache_entry_save_other_fd (const struct disfluid_cache_entry *entry, int fd)
+{
+ char *set_request_date;
+ char *set_response_date;
+ char *set_invalidated;
+ char *meta_header;
+ int status;
+ status =
+ asprintf (&set_request_date, "Request-Date: %lld.%09ld\r\n",
+ (long long) entry->request_date.tv_sec,
+ (long) entry->request_date.tv_nsec);
+ if (status < 0)
+ {
+ abort ();
+ }
+ status =
+ asprintf (&set_response_date, "Response-Date: %lld.%09ld\r\n",
+ (long long) entry->response_date.tv_sec,
+ (long) entry->response_date.tv_nsec);
+ if (status < 0)
+ {
+ abort ();
+ }
+ if (entry->invalidated)
+ {
+ status = asprintf (&set_invalidated, "Invalidated: true\r\n");
+ if (status < 0)
+ {
+ abort ();
+ }
+ }
+ else
+ {
+ set_invalidated = strdup ("");
+ if (set_invalidated == NULL)
+ {
+ abort ();
+ }
+ }
+ status =
+ asprintf (&meta_header, "%s%s%s\r\n", set_request_date, set_response_date,
+ set_invalidated);
+ free (set_request_date);
+ free (set_response_date);
+ free (set_invalidated);
+ if (status < 0)
+ {
+ abort ();
+ }
+ size_t n_written = 0;
+ while (n_written < strlen (meta_header))
+ {
+ ssize_t n_written_this_time = write (fd, &(meta_header[n_written]),
+ strlen (meta_header) - n_written);
+ if (n_written_this_time <= 0)
+ {
+ free (meta_header);
+ return 2;
+ }
+ n_written += n_written_this_time;
+ }
+ free (meta_header);
+ return 0;
+}
+
+static void
+cache_entry_set_request_date (struct disfluid_cache_entry *entry,
+ const struct timespec *date)
+{
+ entry->request_date.tv_sec = date->tv_sec;
+ entry->request_date.tv_nsec = date->tv_nsec;
+}
+
+static void
+cache_entry_set_response_date (struct disfluid_cache_entry *entry,
+ const struct timespec *date)
+{
+ entry->response_date.tv_sec = date->tv_sec;
+ entry->response_date.tv_nsec = date->tv_nsec;
+}
+
+static void
+cache_entry_invalidate (struct disfluid_cache_entry *entry)
+{
+ cache_entry_set_invalidated (entry, true);
+}
+
+static void
+cache_entry_set_invalidated (struct disfluid_cache_entry *entry,
+ bool invalidated)
+{
+ entry->invalidated = invalidated;
+}
+
+static void
+cache_entry_get_request_date (const struct disfluid_cache_entry *entry,
+ struct timespec *date)
+{
+ date->tv_sec = entry->request_date.tv_sec;
+ date->tv_nsec = entry->request_date.tv_nsec;
+}
+
+static void
+cache_entry_get_response_date (const struct disfluid_cache_entry *entry,
+ struct timespec *date)
+{
+ date->tv_sec = entry->response_date.tv_sec;
+ date->tv_nsec = entry->response_date.tv_nsec;
+}
+
+static bool
+cache_entry_invalidated (const struct disfluid_cache_entry *entry)
+{
+ return entry->invalidated;
+}
+
+#endif /* DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED */
diff --git a/src/libdisfluid/disfluid-tests.h b/src/libdisfluid/disfluid-tests.h
index e17c70e..0a40f20 100644
--- a/src/libdisfluid/disfluid-tests.h
+++ b/src/libdisfluid/disfluid-tests.h
@@ -8,10 +8,45 @@ static inline char *run_tests (size_t *n_tests, size_t *n_errors);
# include <check.h>
# include "disfluid-init.h"
+# include "disfluid-version.h"
+# include "disfluid-cache-entry.h"
+
+static inline int
+test_open_file_with_content (const char *content)
+{
+ char name[] = "/tmp/disfluid-cache-entry-test-XXXXXX";
+ int fd = mkstemp (name);
+ if (fd < 0)
+ {
+ abort ();
+ }
+ size_t n_written = 0;
+ while (n_written < strlen (content))
+ {
+ ssize_t next =
+ write (fd, &(content[n_written]), strlen (content) - n_written);
+ if (next <= 0)
+ {
+ abort ();
+ }
+ n_written += next;
+ }
+ remove (name);
+ lseek (fd, 0, SEEK_SET);
+ return fd;
+}
+
+static inline void
+fwrite_text (FILE * file, const char *text)
+{
+ fwrite (text, 1, strlen (text), file);
+ fflush (file);
+}
/* *INDENT-OFF* */
START_TEST (test_check_version)
/* *INDENT-ON* */
+
{
const char *lib_version = version ();
ck_assert_str_eq (lib_version, VERSION);
@@ -20,6 +55,244 @@ START_TEST (test_check_version)
END_TEST
/* *INDENT-ON* */
+/* *INDENT-OFF* */
+START_TEST (test_read_invalid_cache_entry_file)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("Hello, world!");
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ ck_assert_ptr_null (entry);
+ close (fd_source);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_read_cache_entry_no_date)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("Invalidated: true\r\n\r\n");
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ ck_assert_ptr_ne (entry, NULL);
+ close (fd_source);
+ struct timespec request_date = {.tv_sec = 42,.tv_nsec = 69 };
+ struct timespec response_date = {.tv_sec = 42,.tv_nsec = 69 };
+ disfluid_cache_entry_get_request_date (entry, &request_date);
+ disfluid_cache_entry_get_request_date (entry, &response_date);
+ ck_assert_int_eq (request_date.tv_sec, 0);
+ ck_assert_int_eq (request_date.tv_nsec, 0);
+ ck_assert_int_eq (response_date.tv_sec, 0);
+ ck_assert_int_eq (response_date.tv_nsec, 0);
+ ck_assert ((bool) disfluid_cache_entry_invalidated (entry));
+ disfluid_cache_entry_free (entry);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_read_cache_entry_whole_seconds)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("\
+Request-Date: 42\r\n\
+Response-Date: 69\r\n\
+Invalidated: true\r\n\
+\r\n");
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ close (fd_source);
+ ck_assert_ptr_ne (entry, NULL);
+ struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 };
+ struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 };
+ disfluid_cache_entry_get_request_date (entry, &request_date);
+ disfluid_cache_entry_get_response_date (entry, &response_date);
+ ck_assert_int_eq (request_date.tv_sec, 42);
+ ck_assert_int_eq (request_date.tv_nsec, 0);
+ ck_assert_int_eq (response_date.tv_sec, 69);
+ ck_assert_int_eq (response_date.tv_nsec, 0);
+ ck_assert ((bool) disfluid_cache_entry_invalidated (entry));
+ disfluid_cache_entry_free (entry);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_read_cache_entry_whole_seconds_dot)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("\
+Request-Date: 42.\r\n\
+Response-Date: 69.\r\n\
+Invalidated: true\r\n\
+\r\n");
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ close (fd_source);
+ ck_assert_ptr_ne (entry, NULL);
+ struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 };
+ struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 };
+ disfluid_cache_entry_get_request_date (entry, &request_date);
+ disfluid_cache_entry_get_response_date (entry, &response_date);
+ ck_assert_int_eq (request_date.tv_sec, 42);
+ ck_assert_int_eq (request_date.tv_nsec, 0);
+ ck_assert_int_eq (response_date.tv_sec, 69);
+ ck_assert_int_eq (response_date.tv_nsec, 0);
+ ck_assert ((bool) disfluid_cache_entry_invalidated (entry));
+ disfluid_cache_entry_free (entry);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_read_cache_entry_milliseconds)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("\
+Request-Date: 42.123\r\n\
+Response-Date: 69.456\r\n\
+Invalidated: true\r\n\
+\r\n");
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ close (fd_source);
+ ck_assert_ptr_ne (entry, NULL);
+ struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 };
+ struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 };
+ disfluid_cache_entry_get_request_date (entry, &request_date);
+ disfluid_cache_entry_get_response_date (entry, &response_date);
+ ck_assert_int_eq (request_date.tv_sec, 42);
+ ck_assert_int_eq (request_date.tv_nsec, 123000000);
+ ck_assert_int_eq (response_date.tv_sec, 69);
+ ck_assert_int_eq (response_date.tv_nsec, 456000000);
+ ck_assert ((bool) disfluid_cache_entry_invalidated (entry));
+ disfluid_cache_entry_free (entry);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_read_cache_entry_precise)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("\
+Request-Date: 42.123456789\r\n\
+Response-Date: 69.456789123\r\n\
+Invalidated: true\r\n\
+\r\n");
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ close (fd_source);
+ ck_assert_ptr_ne (entry, NULL);
+ struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 };
+ struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 };
+ disfluid_cache_entry_get_request_date (entry, &request_date);
+ disfluid_cache_entry_get_response_date (entry, &response_date);
+ ck_assert_int_eq (request_date.tv_sec, 42);
+ ck_assert_int_eq (request_date.tv_nsec, 123456789);
+ ck_assert_int_eq (response_date.tv_sec, 69);
+ ck_assert_int_eq (response_date.tv_nsec, 456789123);
+ ck_assert ((bool) disfluid_cache_entry_invalidated (entry));
+ disfluid_cache_entry_free (entry);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_read_cache_entry_ambiguous)
+/* *INDENT-ON* */
+
+{
+ int fd_source = test_open_file_with_content ("\
+Request-Date: 42.123456789\r\n\
+Response-Date: 69.456789123\r\n");
+ /* Notice how we don’t know if we’re at the end because of a missing
+ \r\n. */
+ if (fd_source < 0)
+ {
+ abort ();
+ }
+ /* Is it the end? */
+ struct disfluid_cache_entry *entry =
+ disfluid_cache_entry_from_fd (fd_source);
+ ck_assert_ptr_eq (entry, NULL);
+ close (fd_source);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+START_TEST (test_write_cache_entry)
+/* *INDENT-ON* */
+
+{
+ int fd_dest = test_open_file_with_content ("hehe");
+ if (fd_dest < 0)
+ {
+ abort ();
+ }
+ struct disfluid_cache_entry *entry = disfluid_cache_entry_alloc ();
+ if (entry == NULL)
+ {
+ abort ();
+ }
+ const struct timespec rq = {
+ .tv_sec = 123,
+ .tv_nsec = 456
+ };
+ const struct timespec rs = {
+ .tv_sec = 789,
+ .tv_nsec = 101112
+ };
+ disfluid_cache_entry_set_request_date (entry, &rq);
+ disfluid_cache_entry_set_response_date (entry, &rs);
+ int error = disfluid_cache_entry_save_other_fd (entry, fd_dest);
+ ck_assert_int_eq (error, 0);
+ disfluid_cache_entry_free (entry);
+ close (fd_dest);
+}
+/* *INDENT-OFF* */
+END_TEST
+/* *INDENT-ON* */
+
static inline char *
tests_read_whole_file (int file)
{
@@ -53,6 +326,16 @@ run_tests (size_t *n_tests, size_t *n_errors)
TCase *general = tcase_create (_("disfluid general tests"));
tcase_add_test (general, test_check_version);
suite_add_tcase (suite, general);
+ TCase *cache_entry = tcase_create (_("disfluid cache entry files"));
+ tcase_add_test (cache_entry, test_read_invalid_cache_entry_file);
+ tcase_add_test (cache_entry, test_read_cache_entry_no_date);
+ tcase_add_test (cache_entry, test_read_cache_entry_whole_seconds);
+ tcase_add_test (cache_entry, test_read_cache_entry_whole_seconds_dot);
+ tcase_add_test (cache_entry, test_read_cache_entry_milliseconds);
+ tcase_add_test (cache_entry, test_read_cache_entry_precise);
+ tcase_add_test (cache_entry, test_read_cache_entry_ambiguous);
+ tcase_add_test (cache_entry, test_write_cache_entry);
+ suite_add_tcase (suite, cache_entry);
SRunner *runner = srunner_create (suite);
char log_file_name[] = "/tmp/disfluid-unit-tests-XXXXXX";
int log_file = mkstemp (log_file_name);
diff --git a/src/libdisfluid/main.c b/src/libdisfluid/main.c
index 666061c..7a3753b 100644
--- a/src/libdisfluid/main.c
+++ b/src/libdisfluid/main.c
@@ -1,23 +1,28 @@
#include <config.h>
-#include "attribute.h"
-#include <disfluid.h>
-
#define STREQ(a, b) (strcmp ((a), (b)) == 0)
#define STRNEQ(a, b) (! (STREQ (a, b)))
+#include <errno.h>
+#include <fcntl.h>
#include <locale.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
#include "gettext.h"
#include "relocatable.h"
+#include "attribute.h"
+#include <disfluid.h>
#define _(String) dgettext (PACKAGE, (String))
#define N_(String) (String)
-#include "disfluid-version.h"
-#include "disfluid-tests.h"
#include "disfluid-authors.h"
+#include "disfluid-cache-entry.h"
+#include "disfluid-tests.h"
+#include "disfluid-version.h"
const char *
disfluid_version (void)
@@ -108,3 +113,88 @@ disfluid_translation_credits (void)
{
return translation_credits ();
}
+
+struct disfluid_cache_entry *
+disfluid_cache_entry_alloc (void)
+{
+ return cache_entry_alloc ();
+}
+
+void
+disfluid_cache_entry_free (struct disfluid_cache_entry *entry)
+{
+ return cache_entry_free (entry);
+}
+
+struct disfluid_cache_entry *
+disfluid_cache_entry_from_file_name (const char *file_name)
+{
+ return cache_entry_from_file_name (file_name);
+}
+
+struct disfluid_cache_entry *
+disfluid_cache_entry_from_fd (int fd)
+{
+ return cache_entry_from_fd (fd);
+}
+
+int
+disfluid_cache_entry_save_other_file_name (const struct disfluid_cache_entry
+ *entry, const char *file_name)
+{
+ return cache_entry_save_other_file_name (entry, file_name);
+}
+
+int
+disfluid_cache_entry_save_other_fd (const struct disfluid_cache_entry *entry,
+ int fd)
+{
+ return cache_entry_save_other_fd (entry, fd);
+}
+
+void
+disfluid_cache_entry_set_request_date (struct disfluid_cache_entry *entry,
+ const struct timespec *date)
+{
+ cache_entry_set_request_date (entry, date);
+}
+
+void
+disfluid_cache_entry_set_response_date (struct disfluid_cache_entry *entry,
+ const struct timespec *date)
+{
+ cache_entry_set_response_date (entry, date);
+}
+
+void
+disfluid_cache_entry_invalidate (struct disfluid_cache_entry *entry)
+{
+ cache_entry_invalidate (entry);
+}
+
+void
+disfluid_cache_entry_set_invalidated (struct disfluid_cache_entry *entry,
+ int invalidated)
+{
+ cache_entry_set_invalidated (entry, invalidated);
+}
+
+void
+disfluid_cache_entry_get_request_date (const struct disfluid_cache_entry
+ *entry, struct timespec *date)
+{
+ cache_entry_get_request_date (entry, date);
+}
+
+void
+disfluid_cache_entry_get_response_date (const struct disfluid_cache_entry
+ *entry, struct timespec *date)
+{
+ cache_entry_get_response_date (entry, date);
+}
+
+int
+disfluid_cache_entry_invalidated (const struct disfluid_cache_entry *entry)
+{
+ return cache_entry_invalidated (entry);
+}