Skip to content

Commit

Permalink
feat: Implementing lazy parsing for date releated headers and updatin…
Browse files Browse the repository at this point in the history
…g tests
  • Loading branch information
klkucaj committed Dec 17, 2024
1 parent d010e32 commit 08bb824
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 41 deletions.
155 changes: 115 additions & 40 deletions lib/src/headers/headers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,18 @@ abstract base class Headers {

/// Define header properties
/// Date-related headers
final DateTime? date;
final DateTime? expires;
final DateTime? ifModifiedSince;
final DateTime? lastModified;
/// Date-related headers`
final _LazyInit<DateTime?> _date;
DateTime? get date => _date.value;

final _LazyInit<DateTime?> _expires;
DateTime? get expires => _expires.value;

final _LazyInit<DateTime?> _ifModifiedSince;
DateTime? get ifModifiedSince => _ifModifiedSince.value;

final _LazyInit<DateTime?> _lastModified;
DateTime? get lastModified => _lastModified.value;

/// General Headers
final Uri? origin;
Expand Down Expand Up @@ -264,10 +271,10 @@ abstract base class Headers {

Headers._({
// Date-related headers
this.date,
this.expires,
this.ifModifiedSince,
this.lastModified,
required _LazyInit<DateTime?> date,
required _LazyInit<DateTime?> expires,
required _LazyInit<DateTime?> ifModifiedSince,
required _LazyInit<DateTime?> lastModified,

// General Headers
this.origin,
Expand Down Expand Up @@ -340,7 +347,11 @@ abstract base class Headers {
this.crossOriginEmbedderPolicy,
this.crossOriginOpenerPolicy,
required this.failedHeadersToParse,
}) : custom = custom ?? CustomHeaders.empty();
}) : _date = date,
_expires = expires,
_ifModifiedSince = ifModifiedSince,
_lastModified = lastModified,
custom = custom ?? CustomHeaders.empty();

/// Create a new request headers instance from a Dart IO request
factory Headers.fromHttpRequest(
Expand Down Expand Up @@ -368,21 +379,29 @@ abstract base class Headers {

return _HeadersImpl(
// Date-related headers
date: dartIOHeaders.parseSingleValue(
dateHeader,
onParse: parseDate,
),
expires: dartIOHeaders.parseSingleValue(
expiresHeader,
onParse: parseDate,
),
ifModifiedSince: dartIOHeaders.parseSingleValue(
ifModifiedSinceHeader,
onParse: parseDate,
),
lastModified: dartIOHeaders.parseSingleValue(
lastModifiedHeader,
onParse: parseDate,
date: _LazyInit.lazy(
init: () => dartIOHeaders.parseSingleValue(
dateHeader,
onParse: parseDate,
),
),
expires: _LazyInit.lazy(
init: () => dartIOHeaders.parseSingleValue(
expiresHeader,
onParse: parseDate,
),
),
ifModifiedSince: _LazyInit.lazy(
init: () => dartIOHeaders.parseSingleValue(
ifModifiedSinceHeader,
onParse: parseDate,
),
),
lastModified: _LazyInit.lazy(
init: () => dartIOHeaders.parseSingleValue(
lastModifiedHeader,
onParse: parseDate,
),
),

// General Headers
Expand Down Expand Up @@ -687,8 +706,10 @@ abstract base class Headers {
}) {
return _HeadersImpl(
// Date-related headers
date: date,
ifModifiedSince: ifModifiedSince,
date: _LazyInit.value(value: date),
ifModifiedSince: _LazyInit.value(value: ifModifiedSince),
expires: _LazyInit.nullValue(),
lastModified: _LazyInit.nullValue(),

// Request Headers
xPoweredBy: xPoweredBy,
Expand Down Expand Up @@ -782,9 +803,12 @@ abstract base class Headers {
CrossOriginOpenerPolicyHeader? crossOriginOpenerPolicy,
}) {
return _HeadersImpl(
date: date ?? DateTime.now(),
expires: expires,
lastModified: lastModified,
// Date-related headers
date: _LazyInit.value(value: date ?? DateTime.now()),
expires: _LazyInit.value(value: expires),
lastModified: _LazyInit.value(value: lastModified),
ifModifiedSince: _LazyInit.nullValue(),

origin: origin,
server: server,
via: via,
Expand Down Expand Up @@ -920,10 +944,10 @@ abstract base class Headers {
final class _HeadersImpl extends Headers {
_HeadersImpl({
/// Date-related headers
super.date,
super.expires,
super.ifModifiedSince,
super.lastModified,
required super.date,
required super.expires,
required super.ifModifiedSince,
required super.lastModified,

/// General Headers
super.origin,
Expand Down Expand Up @@ -1077,12 +1101,15 @@ final class _HeadersImpl extends Headers {
Object? crossOriginOpenerPolicy = _Undefined,
}) {
return _HeadersImpl(
date: date is DateTime? ? date : this.date,
expires: expires is DateTime? ? expires : this.expires,
ifModifiedSince:
ifModifiedSince is DateTime? ? ifModifiedSince : this.ifModifiedSince,
lastModified:
lastModified is DateTime? ? lastModified : this.lastModified,
date: date is DateTime? ? _LazyInit.value(value: date) : _date,
expires:
expires is DateTime? ? _LazyInit.value(value: expires) : _expires,
ifModifiedSince: ifModifiedSince is DateTime?
? _LazyInit.value(value: ifModifiedSince)
: _ifModifiedSince,
lastModified: lastModified is DateTime?
? _LazyInit.value(value: lastModified)
: _lastModified,

/// General Headers
origin: origin is Uri? ? origin : this.origin,
Expand Down Expand Up @@ -1478,3 +1505,51 @@ final class _HeadersImpl extends Headers {
}

class _Undefined {}

typedef _LazyInitializer<T> = T Function();

class _LazyInit<T> {
final _LazyInitializer<T>? _init;
bool _isInitialized = false;
T? _value;

_LazyInit._({
_LazyInitializer<T>? init,
T? value,
bool isInitialized = false,
}) : _init = init,
_value = value,
_isInitialized = isInitialized;

factory _LazyInit.value({
required T value,
}) {
return _LazyInit._(
value: value,
isInitialized: true,
);
}

factory _LazyInit.lazy({
required _LazyInitializer<T> init,
}) {
return _LazyInit._(
init: init,
isInitialized: false,
);
}

factory _LazyInit.nullValue() {
return _LazyInit._(
isInitialized: true,
);
}

T? get value {
if (!_isInitialized) {
_value = _init?.call();
_isInitialized = true;
}
return _value;
}
}
15 changes: 15 additions & 0 deletions test/headers/basic/date_header_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ void main() {
},
);

test(
'when a Date header with an invalid date format is passed '
'then the server does not respond with a bad request if the headers '
'is not actually used',
() async {
Headers headers = await getServerRequestHeaders(
server: server,
headers: {'date': 'invalid-date-format'},
echoHeaders: false,
);

expect(headers, isNotNull);
},
);

test(
'when a valid Date header is passed then it should parse the date correctly',
() async {
Expand Down
15 changes: 15 additions & 0 deletions test/headers/basic/expires_header_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ void main() {
},
);

test(
'when an Expires header with an invalid date format is passed '
'then the server does not respond with a bad request if the headers '
'is not actually used',
() async {
Headers headers = await getServerRequestHeaders(
server: server,
headers: {'expires': 'invalid-date-format'},
echoHeaders: false,
);

expect(headers, isNotNull);
},
);

test(
'when a valid Expires header is passed then it should parse the date correctly',
() async {
Expand Down
15 changes: 15 additions & 0 deletions test/headers/basic/if_modified_since_header_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ void main() {
},
);

test(
'when an If-Modified-Since header with an invalid date format is passed '
'then the server does not respond with a bad request if the headers '
'is not actually used',
() async {
Headers headers = await getServerRequestHeaders(
server: server,
headers: {'if-modified-since': 'invalid-date-format'},
echoHeaders: false,
);

expect(headers, isNotNull);
},
);

test(
'when a valid If-Modified-Since header is passed then it should parse the '
'date correctly',
Expand Down
15 changes: 15 additions & 0 deletions test/headers/basic/last_modified_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ void main() {
},
);

test(
'when a Last-Modified header with an invalid date format is passed '
'then the server does not respond with a bad request if the headers '
'is not actually used',
() async {
Headers headers = await getServerRequestHeaders(
server: server,
headers: {'last-modified': 'invalid-date-format'},
echoHeaders: false,
);

expect(headers, isNotNull);
},
);

test(
'when a valid Last-Modified header is passed then it should parse the '
'date correctly',
Expand Down
6 changes: 5 additions & 1 deletion test/headers/headers_test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ class BadRequestException implements Exception {
Future<Headers> getServerRequestHeaders({
required RelicServer server,
required Map<String, String> headers,
// Whether to echo the headers back to the client.
bool echoHeaders = true,
}) async {
Headers? parsedHeaders;

server.mountAndStart(
(Request request) {
parsedHeaders = request.headers;
return Response.ok();
return Response.ok(
headers: echoHeaders ? parsedHeaders : null,
);
},
);

Expand Down

0 comments on commit 08bb824

Please sign in to comment.