tests: Split RFC 2616 date parsing code out and add tests
This makes it testable, and increases its test coverage too 100% of lines, as measured by `make coverage`. Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This commit is contained in:
1 parent
92215025aa
commit
0974a7faf1
7 files changed
+340
-137
No files matched your search
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright © 2020 Endless OS Foundation LLC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <pwithnall@endlessos.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __GI_SCANNER__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GDateTime *_ostree_parse_rfc2616_date_time (const char *buf,
|
||||
size_t len);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright © 2020 Endless OS Foundation LLC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <pwithnall@endlessos.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ostree-date-utils-private.h"
|
||||
|
||||
/* @buf must already be known to be long enough */
|
||||
static gboolean
|
||||
parse_uint (const char *buf,
|
||||
guint n_digits,
|
||||
guint min,
|
||||
guint max,
|
||||
guint *out)
|
||||
{
|
||||
guint64 number;
|
||||
const char *end_ptr = NULL;
|
||||
gint saved_errno = 0;
|
||||
|
||||
g_return_val_if_fail (n_digits == 2 || n_digits == 4, FALSE);
|
||||
g_return_val_if_fail (out != NULL, FALSE);
|
||||
|
||||
errno = 0;
|
||||
number = g_ascii_strtoull (buf, (gchar **)&end_ptr, 10);
|
||||
saved_errno = errno;
|
||||
|
||||
if (!g_ascii_isdigit (buf[0]) ||
|
||||
saved_errno != 0 ||
|
||||
end_ptr == NULL ||
|
||||
end_ptr != buf + n_digits ||
|
||||
number < min ||
|
||||
number > max)
|
||||
return FALSE;
|
||||
|
||||
*out = number;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Locale-independent parsing for RFC 2616 date/times.
|
||||
*
|
||||
* Reference: https://tools.ietf.org/html/rfc2616#section-3.3.1
|
||||
*
|
||||
* Syntax:
|
||||
* <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
|
||||
*
|
||||
* Note that this only accepts the full-year and GMT formats specified by
|
||||
* RFC 1123. It doesn’t accept RFC 850 or asctime formats.
|
||||
*
|
||||
* Example:
|
||||
* Wed, 21 Oct 2015 07:28:00 GMT
|
||||
*/
|
||||
GDateTime *
|
||||
_ostree_parse_rfc2616_date_time (const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
guint day_int, year_int, hour_int, minute_int, second_int;
|
||||
const char *day_names[] =
|
||||
{
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
"Sun",
|
||||
};
|
||||
size_t day_name_index;
|
||||
const char *month_names[] =
|
||||
{
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
};
|
||||
size_t month_name_index;
|
||||
|
||||
if (len != 29)
|
||||
return NULL;
|
||||
|
||||
const char *day_name = buf;
|
||||
const char *day = buf + 5;
|
||||
const char *month_name = day + 3;
|
||||
const char *year = month_name + 4;
|
||||
const char *hour = year + 5;
|
||||
const char *minute = hour + 3;
|
||||
const char *second = minute + 3;
|
||||
const char *tz = second + 3;
|
||||
|
||||
for (day_name_index = 0; day_name_index < G_N_ELEMENTS (day_names); day_name_index++)
|
||||
{
|
||||
if (strncmp (day_names[day_name_index], day_name, 3) == 0)
|
||||
break;
|
||||
}
|
||||
if (day_name_index >= G_N_ELEMENTS (day_names))
|
||||
return NULL;
|
||||
/* don’t validate whether the day_name matches the rest of the date */
|
||||
if (*(day_name + 3) != ',' || *(day_name + 4) != ' ')
|
||||
return NULL;
|
||||
if (!parse_uint (day, 2, 1, 31, &day_int))
|
||||
return NULL;
|
||||
if (*(day + 2) != ' ')
|
||||
return NULL;
|
||||
for (month_name_index = 0; month_name_index < G_N_ELEMENTS (month_names); month_name_index++)
|
||||
{
|
||||
if (strncmp (month_names[month_name_index], month_name, 3) == 0)
|
||||
break;
|
||||
}
|
||||
if (month_name_index >= G_N_ELEMENTS (month_names))
|
||||
return NULL;
|
||||
if (*(month_name + 3) != ' ')
|
||||
return NULL;
|
||||
if (!parse_uint (year, 4, 0, 9999, &year_int))
|
||||
return NULL;
|
||||
if (*(year + 4) != ' ')
|
||||
return NULL;
|
||||
if (!parse_uint (hour, 2, 0, 23, &hour_int))
|
||||
return NULL;
|
||||
if (*(hour + 2) != ':')
|
||||
return NULL;
|
||||
if (!parse_uint (minute, 2, 0, 59, &minute_int))
|
||||
return NULL;
|
||||
if (*(minute + 2) != ':')
|
||||
return NULL;
|
||||
if (!parse_uint (second, 2, 0, 60, &second_int)) /* allow leap seconds */
|
||||
return NULL;
|
||||
if (*(second + 2) != ' ')
|
||||
return NULL;
|
||||
if (strncmp (tz, "GMT", 3) != 0)
|
||||
return NULL;
|
||||
|
||||
return g_date_time_new_utc (year_int, month_name_index + 1, day_int,
|
||||
hour_int, minute_int, second_int);
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
#define CURLPIPE_MULTIPLEX 0
|
||||
#endif
|
||||
|
||||
#include "ostree-date-utils-private.h"
|
||||
#include "ostree-fetcher.h"
|
||||
#include "ostree-fetcher-util.h"
|
||||
#include "ostree-enumtypes.h"
|
||||
@@ -591,141 +592,6 @@ write_cb (void *ptr, size_t size, size_t nmemb, void *data)
|
||||
return realsize;
|
||||
}
|
||||
|
||||
/* @buf must already be known to be long enough */
|
||||
static gboolean
|
||||
parse_uint (const char *buf,
|
||||
guint n_digits,
|
||||
guint min,
|
||||
guint max,
|
||||
guint *out)
|
||||
{
|
||||
guint64 number;
|
||||
const char *end_ptr = NULL;
|
||||
gint saved_errno = 0;
|
||||
|
||||
g_return_val_if_fail (n_digits == 2 || n_digits == 4, FALSE);
|
||||
g_return_val_if_fail (out != NULL, FALSE);
|
||||
|
||||
errno = 0;
|
||||
number = g_ascii_strtoull (buf, (gchar **)&end_ptr, 10);
|
||||
saved_errno = errno;
|
||||
|
||||
if (!g_ascii_isdigit (buf[0]) ||
|
||||
saved_errno != 0 ||
|
||||
end_ptr == NULL ||
|
||||
end_ptr != buf + n_digits ||
|
||||
number < min ||
|
||||
number > max)
|
||||
return FALSE;
|
||||
|
||||
*out = number;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Locale-independent parsing for RFC 2616 date/times.
|
||||
*
|
||||
* Reference: https://tools.ietf.org/html/rfc2616#section-3.3.1
|
||||
*
|
||||
* Syntax:
|
||||
* <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
|
||||
*
|
||||
* Note that this only accepts the full-year and GMT formats specified by
|
||||
* RFC 1123. It doesn’t accept RFC 850 or asctime formats.
|
||||
*
|
||||
* Example:
|
||||
* Wed, 21 Oct 2015 07:28:00 GMT
|
||||
*/
|
||||
static GDateTime *
|
||||
parse_rfc2616_date_time (const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
guint day_int, year_int, hour_int, minute_int, second_int;
|
||||
const char *day_names[] =
|
||||
{
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
"Sun",
|
||||
};
|
||||
size_t day_name_index;
|
||||
const char *month_names[] =
|
||||
{
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
};
|
||||
size_t month_name_index;
|
||||
|
||||
if (len != 29)
|
||||
return NULL;
|
||||
|
||||
const char *day_name = buf;
|
||||
const char *day = buf + 5;
|
||||
const char *month_name = day + 3;
|
||||
const char *year = month_name + 4;
|
||||
const char *hour = year + 5;
|
||||
const char *minute = hour + 3;
|
||||
const char *second = minute + 3;
|
||||
const char *tz = second + 3;
|
||||
|
||||
for (day_name_index = 0; day_name_index < G_N_ELEMENTS (day_names); day_name_index++)
|
||||
{
|
||||
if (strncmp (day_names[day_name_index], day_name, 3) == 0)
|
||||
break;
|
||||
}
|
||||
if (day_name_index >= G_N_ELEMENTS (day_names))
|
||||
return NULL;
|
||||
/* don’t validate whether the day_name matches the rest of the date */
|
||||
if (*(day_name + 3) != ',' || *(day_name + 4) != ' ')
|
||||
return NULL;
|
||||
if (!parse_uint (day, 2, 1, 31, &day_int))
|
||||
return NULL;
|
||||
if (*(day + 2) != ' ')
|
||||
return NULL;
|
||||
for (month_name_index = 0; month_name_index < G_N_ELEMENTS (month_names); month_name_index++)
|
||||
{
|
||||
if (strncmp (month_names[month_name_index], month_name, 3) == 0)
|
||||
break;
|
||||
}
|
||||
if (month_name_index >= G_N_ELEMENTS (month_names))
|
||||
return NULL;
|
||||
if (*(month_name + 3) != ' ')
|
||||
return NULL;
|
||||
if (!parse_uint (year, 4, 0, 9999, &year_int))
|
||||
return NULL;
|
||||
if (*(year + 4) != ' ')
|
||||
return NULL;
|
||||
if (!parse_uint (hour, 2, 0, 23, &hour_int))
|
||||
return NULL;
|
||||
if (*(hour + 2) != ':')
|
||||
return NULL;
|
||||
if (!parse_uint (minute, 2, 0, 59, &minute_int))
|
||||
return NULL;
|
||||
if (*(minute + 2) != ':')
|
||||
return NULL;
|
||||
if (!parse_uint (second, 2, 0, 60, &second_int)) /* allow leap seconds */
|
||||
return NULL;
|
||||
if (*(second + 2) != ' ')
|
||||
return NULL;
|
||||
if (strncmp (tz, "GMT", 3) != 0)
|
||||
return NULL;
|
||||
|
||||
return g_date_time_new_utc (year_int, month_name_index + 1, day_int,
|
||||
hour_int, minute_int, second_int);
|
||||
}
|
||||
|
||||
/* CURLOPT_HEADERFUNCTION */
|
||||
static size_t
|
||||
response_header_cb (const char *buffer, size_t size, size_t n_items, void *user_data)
|
||||
@@ -753,7 +619,7 @@ response_header_cb (const char *buffer, size_t size, size_t n_items, void *user_
|
||||
strncasecmp (buffer, last_modified_header, strlen (last_modified_header)) == 0)
|
||||
{
|
||||
g_autofree char *lm_buf = g_strstrip (g_strdup (buffer + strlen (last_modified_header)));
|
||||
g_autoptr(GDateTime) dt = parse_rfc2616_date_time (lm_buf, strlen (lm_buf));
|
||||
g_autoptr(GDateTime) dt = _ostree_parse_rfc2616_date_time (lm_buf, strlen (lm_buf));
|
||||
req->out_last_modified = (dt != NULL) ? g_date_time_to_unix (dt) : 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user