From 100b22e6c23c44a082fd69b8c05a63c7492083f7 Mon Sep 17 00:00:00 2001 From: Frank Berghaus Date: Wed, 21 Sep 2022 18:50:17 +0200 Subject: Use case-insensitive comparison for headers fix #2100 (#2101) * Use case-inseneitive comparison for headers HTTP headers should be case-insensitive. Use case-insensitive string comparisons when working with HTTP header responses to ensure compatibility. * Revert "Use case-inseneitive comparison for headers" This reverts commit a02f591723506b62b7208449be6eef7122120398 * Ignore case when searching HTTP header responses Looking up the Content-Length in the header returned by S3 storage endpoints should ignore case. To guarantee portability implement a function for case-insensitive string search, because it is non-standard. * Add an _ after H5 for the strcasestr implementation It is a private function and should sport that underscore. * Remove author comment from the doc comment * Use search function defined by system if available Check whether the system provides a function implementing case insensitive string searches. Only use the custom implementation if the system does not provide the functionality. * Add tests for case-insensitive search Basic tests: - Search for empty string - Search with exact match - Search with case-insensitive match - search with no match * Enforce clang-format style Some variable definitions in the th5_system tests did not conform to clang-format's expectations. Updated the offending lines. * Correct comment describing test case * Added some spaces to please clang-format * Ignore discarding const Ask the compiler to ignore discarding the const when retunring a match from H5_strcasestr Co-authored-by: Frank Berghaus --- config/cmake/ConfigureChecks.cmake | 1 + config/cmake/H5pubconf.h.in | 3 +++ configure.ac | 2 +- src/H5FDs3comms.c | 2 +- src/H5private.h | 8 ++++++++ src/H5system.c | 39 ++++++++++++++++++++++++++++++++++++++ src/H5win32defs.h | 1 + test/th5_system.c | 26 +++++++++++++++++++++++++ 8 files changed, 80 insertions(+), 2 deletions(-) diff --git a/config/cmake/ConfigureChecks.cmake b/config/cmake/ConfigureChecks.cmake index d71ca08..ee90128 100644 --- a/config/cmake/ConfigureChecks.cmake +++ b/config/cmake/ConfigureChecks.cmake @@ -455,6 +455,7 @@ CHECK_FUNCTION_EXISTS (sigsetjmp ${HDF_PREFIX}_HAVE_SIGSETJMP) CHECK_FUNCTION_EXISTS (sigprocmask ${HDF_PREFIX}_HAVE_SIGPROCMASK) CHECK_FUNCTION_EXISTS (srandom ${HDF_PREFIX}_HAVE_SRANDOM) +CHECK_FUNCTION_EXISTS (strcasestr ${HDF_PREFIX}_HAVE_STRCASESTR) CHECK_FUNCTION_EXISTS (strdup ${HDF_PREFIX}_HAVE_STRDUP) CHECK_FUNCTION_EXISTS (symlink ${HDF_PREFIX}_HAVE_SYMLINK) diff --git a/config/cmake/H5pubconf.h.in b/config/cmake/H5pubconf.h.in index a500f46..1f2e1b5 100644 --- a/config/cmake/H5pubconf.h.in +++ b/config/cmake/H5pubconf.h.in @@ -313,6 +313,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_STDLIB_H @H5_HAVE_STDLIB_H@ +/* Define to 1 if you have the `strcasestr' function. */ +#cmakedefine H5_HAVE_STRCASESTR @H5_HAVE_STRCASESTR@ + /* Define to 1 if you have the `strdup' function. */ #cmakedefine H5_HAVE_STRDUP @H5_HAVE_STRDUP@ diff --git a/configure.ac b/configure.ac index cdbdadb..ab9897b 100644 --- a/configure.ac +++ b/configure.ac @@ -2137,7 +2137,7 @@ AC_CHECK_FUNCS([alarm asprintf clock_gettime fcntl flock fork]) AC_CHECK_FUNCS([gethostname getrusage gettimeofday]) AC_CHECK_FUNCS([lstat rand_r random setsysinfo]) AC_CHECK_FUNCS([siglongjmp sigsetjmp sigprocmask]) -AC_CHECK_FUNCS([srandom strdup symlink]) +AC_CHECK_FUNCS([srandom strcasestr strdup symlink]) AC_CHECK_FUNCS([tmpfile vasprintf waitpid]) ## ---------------------------------------------------------------------- diff --git a/src/H5FDs3comms.c b/src/H5FDs3comms.c index 1bf0a2f..628c55a 100644 --- a/src/H5FDs3comms.c +++ b/src/H5FDs3comms.c @@ -941,7 +941,7 @@ H5FD_s3comms_s3r_getsize(s3r_t *handle) * PARSE RESPONSE * ******************/ - start = HDstrstr(headerresponse, "\r\nContent-Length: "); + start = HDstrcasestr(headerresponse, "\r\nContent-Length: "); if (start == NULL) HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not find \"Content-Length\" in response."); diff --git a/src/H5private.h b/src/H5private.h index c2ce4c1..f9ff043 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -593,6 +593,7 @@ H5_DLL herr_t H5_timer_stop(H5_timer_t *timer /*in,out*/); H5_DLL herr_t H5_timer_get_times(H5_timer_t timer, H5_timevals_t *times /*in,out*/); H5_DLL herr_t H5_timer_get_total_times(H5_timer_t timer, H5_timevals_t *times /*in,out*/); H5_DLL char *H5_timer_get_time_string(double seconds); +H5_DLL char *H5_strcasestr(const char *haystack, const char *needle); /* Depth of object copy */ typedef enum { @@ -1385,6 +1386,13 @@ H5_DLL H5_ATTR_CONST int Nflock(int fd, int operation); #ifndef HDstrcat #define HDstrcat(X, Y) strcat(X, Y) #endif +#ifndef HDstrcasestr +#if defined(H5_HAVE_STRCASESTR) +#define HDstrcasestr(X, Y) strcasestr(X, Y) +#else +#define HDstrcasestr(X, Y) H5_strcasestr(X, Y) +#endif +#endif #ifndef HDstrchr #define HDstrchr(S, C) strchr(S, C) #endif diff --git a/src/H5system.c b/src/H5system.c index 9a19860..a7f2edd 100644 --- a/src/H5system.c +++ b/src/H5system.c @@ -1350,3 +1350,42 @@ H5_get_option(int argc, const char *const *argv, const char *opts, const struct /* return the current flag character found */ return optchar; } + +/*------------------------------------------------------------------------- + * Function: H5_strcasestr + * + * Purpose: Find the first occurrence of the substring needle in the + * string haystack ignoring case. + * + * Return: Success: Pointer to the beginning of the located substring + * + * Failure: NULL + * + *------------------------------------------------------------------------- + */ +char * +H5_strcasestr(const char *haystack, const char *needle) +{ + /* Check arguments. */ + HDassert(haystack); + HDassert(needle); + + /* begin once from each character of haystack, until needle is found */ + do { + const char *h = haystack; + const char *n = needle; + /* loop while lowercase strings match, or needle ends */ + while (HDtolower(*h) == HDtolower(*n) && *n) { + h++; + n++; + } + /* if all characters in needle matched we found it */ + if (*n == 0) { + /* must discard const qualifier here, so turn off the warning */ + H5_GCC_CLANG_DIAG_OFF("cast-qual") + return (char *)haystack; + H5_GCC_CLANG_DIAG_ON("cast-qual") + } + } while (*haystack++); + return 0; +} /* end H5_strcasestr() */ diff --git a/src/H5win32defs.h b/src/H5win32defs.h index 1039f23..ea607da 100644 --- a/src/H5win32defs.h +++ b/src/H5win32defs.h @@ -82,6 +82,7 @@ struct timezone { #define HDsleep(S) Sleep(S * 1000) #define HDstat(S, B) _stati64(S, B) #define HDstrcasecmp(A, B) _stricmp(A, B) +#define HDstrcasestr(A, B) StrStrIA(A, B) #define HDstrndup(S, N) H5_strndup(S, N) #define HDstrtok_r(X, Y, Z) strtok_s(X, Y, Z) #define HDtzset() _tzset() diff --git a/test/th5_system.c b/test/th5_system.c index 09570cf..784cb3f 100644 --- a/test/th5_system.c +++ b/test/th5_system.c @@ -438,6 +438,31 @@ test_h5_basename(void) } static void +test_h5_strcasestr(void) +{ + const char *const haystack = "My test string"; + char *str = NULL; + + MESSAGE(5, ("Testing H5_strcasestr\n")); + + /* check that H5_strcasestr returns target in empty search */ + str = H5_strcasestr(haystack, ""); + CHECK_PTR_EQ(str, haystack, "H5_strcasestr search for empty"); + + /* Check that H5_strcasestr find a string of same case */ + str = H5_strcasestr(haystack, "string"); + CHECK_PTR_EQ(str, &(haystack[8]), "H5_strcasestr search same case"); + + /* Check that H5_strcasestr find a string of different case */ + str = H5_strcasestr(haystack, "sTrInG"); + CHECK_PTR_EQ(str, &(haystack[8]), "H5_strcasestr search different case"); + + /* Check that H5_strcasestr returns NULL if no match is found */ + str = H5_strcasestr(haystack, "nomatch"); + CHECK_PTR_NULL(str, "H5_strcasestr search with no match"); +} + +static void test_h5_strndup(void) { #ifdef H5_HAVE_WIN32_API @@ -495,6 +520,7 @@ test_h5_system(void) test_h5_dirname(); test_h5_basename(); + test_h5_strcasestr(); test_h5_strndup(); } -- cgit v0.12