diff options
author | Vivien Kraus <vivien@planete-kraus.eu> | 2023-03-11 11:49:22 +0100 |
---|---|---|
committer | Vivien Kraus <vivien@planete-kraus.eu> | 2023-03-11 14:38:56 +0100 |
commit | 419d7ec40b30e70d0f0f00ad96909c6978c9eace (patch) | |
tree | b4ea7e89b2a18e4e602f757b11b7f5df516e3426 | |
parent | dbaf5022d8b24fdd1672dd00eacb7532fbdc5310 (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.conf | 39 | ||||
-rw-r--r-- | include/disfluid.h | 90 | ||||
-rw-r--r-- | src/adwaita/Makefile.am | 5 | ||||
-rw-r--r-- | src/libdisfluid/Makefile.am | 3 | ||||
-rw-r--r-- | src/libdisfluid/disfluid-cache-entry.h | 504 | ||||
-rw-r--r-- | src/libdisfluid/disfluid-tests.h | 283 | ||||
-rw-r--r-- | src/libdisfluid/main.c | 100 |
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); +} |