Skip to content

Commit

Permalink
Merge pull request #1742 from nextcloud/feat/use_http_parser_for_dates
Browse files Browse the repository at this point in the history
refactor(neon_framework): use http_parser for http date parsing
  • Loading branch information
Leptopoda authored Mar 10, 2024
2 parents 769b2a7 + 5223df7 commit 9e99779
Show file tree
Hide file tree
Showing 8 changed files with 14 additions and 23 deletions.
5 changes: 0 additions & 5 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
"automerge": true
},
"packageRules": [
{
"matchFileNames": ["packages/nextcloud/**"],
"matchPackageNames": ["intl"],
"rangeStrategy": "widen"
},
{
"matchManagers": ["pub"],
"matchDatasources": ["dart-version", "flutter-version"],
Expand Down
8 changes: 2 additions & 6 deletions packages/neon_framework/lib/src/utils/request_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:built_value/serializer.dart';
import 'package:dynamite_runtime/http_client.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:neon_framework/models.dart';
Expand Down Expand Up @@ -44,10 +44,6 @@ const kMaxTries = 3;
/// Requests that take longer than this duration will be canceled.
const kDefaultTimeout = Duration(seconds: 30);

/// Implements https://www.rfc-editor.org/rfc/rfc9110#name-date-time-formats
@visibleForTesting
final httpDateFormat = DateFormat('E, d MMM yyyy HH:mm:ss v', 'en_US');

/// A singleton class that handles requests to the Nextcloud API.
///
/// Requests need to be made through the [nextcloud](https://pub.dev/packages/nextcloud)
Expand Down Expand Up @@ -369,7 +365,7 @@ class CacheParameters {

/// Parse the cache parameters from HTTP response headers.
factory CacheParameters.parseHeaders(Map<String, dynamic> headers) {
final expiry = headers.containsKey('expires') ? httpDateFormat.parse(headers['expires']! as String) : null;
final expiry = headers.containsKey('expires') ? parseHttpDate(headers['expires']! as String) : null;
return CacheParameters(
etag: headers['etag'] as String?,
expires: _isExpired(expiry) ? null : expiry,
Expand Down
1 change: 1 addition & 0 deletions packages/neon_framework/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies:
flutter_zxing: ^1.0.0
go_router: ^13.0.0
http: ^1.0.0
http_parser: ^4.0.0
image: ^4.0.0
intersperse: ^2.0.0
intl: ^0.18.0
Expand Down
5 changes: 3 additions & 2 deletions packages/neon_framework/test/request_manager_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:built_value/serializer.dart';
import 'package:dynamite_runtime/http_client.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart';
import 'package:http_parser/http_parser.dart';
import 'package:mocktail/mocktail.dart';
import 'package:neon_framework/src/bloc/result.dart';
import 'package:neon_framework/src/utils/request_manager.dart';
Expand Down Expand Up @@ -681,7 +682,7 @@ void main() {

test('cache ETag and Expires', () async {
for (final (hours, isSet) in [(1, true), (-1, false)]) {
var newExpires = DateTime.now().add(Duration(hours: hours));
var newExpires = DateTime.timestamp().add(Duration(hours: hours));
// Only precise to the second is allowed.
newExpires = newExpires.subtract(
Duration(
Expand Down Expand Up @@ -713,7 +714,7 @@ void main() {
headers: {},
rawHeaders: {
'etag': 'a',
'expires': httpDateFormat.format(newExpires),
'expires': formatHttpDate(newExpires),
},
),
unwrap: (rawResponse) => rawResponse.body,
Expand Down
3 changes: 2 additions & 1 deletion packages/nextcloud/lib/src/webdav/file.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:http_parser/http_parser.dart';
import 'package:nextcloud/src/webdav/client.dart';
import 'package:nextcloud/src/webdav/path_uri.dart';
import 'package:nextcloud/src/webdav/props.dart';
Expand Down Expand Up @@ -62,7 +63,7 @@ class WebDavFile {
/// Last modified date of the file
late final DateTime? lastModified = () {
if (props.davgetlastmodified != null) {
return webdavDateFormat.parseUtc(props.davgetlastmodified!);
return parseHttpDate(props.davgetlastmodified!);
}
return null;
}();
Expand Down
4 changes: 0 additions & 4 deletions packages/nextcloud/lib/src/webdav/webdav.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
// ignore_for_file: public_member_api_docs
// coverage:ignore-file

import 'package:intl/intl.dart';
import 'package:meta/meta.dart';
import 'package:nextcloud/src/webdav/props.dart';
import 'package:xml/xml.dart';
import 'package:xml_annotation/xml_annotation.dart' as annotation;

part 'webdav.g.dart';

/// Format used in WebDAV
final webdavDateFormat = DateFormat('E, d MMM yyyy HH:mm:ss', 'en_US');

const namespaceDav = 'DAV:';
const namespaceOwncloud = 'http://owncloud.org/ns';
const namespaceNextcloud = 'http://nextcloud.org/ns';
Expand Down
2 changes: 1 addition & 1 deletion packages/nextcloud/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies:
crypton: ^2.0.0
dynamite_runtime: ^0.2.0
http: ^1.2.0
intl: '>=0.17.0 <0.20.0'
http_parser: ^4.0.0
json_annotation: ^4.8.1
meta: ^1.0.0
universal_io: ^2.0.0
Expand Down
9 changes: 5 additions & 4 deletions packages/nextcloud/test/webdav_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';

import 'package:http_parser/http_parser.dart';
import 'package:mocktail/mocktail.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
Expand Down Expand Up @@ -174,7 +175,7 @@ void main() {
responses.singleWhere((response) => response.href!.endsWith('/Nextcloud.png')).propstats.first.prop;
expect(props.nchaspreview, isTrue);
expect(props.davgetcontenttype, 'image/png');
expect(webdavDateFormat.parseUtc(props.davgetlastmodified!).isBefore(DateTime.now()), isTrue);
expect(parseHttpDate(props.davgetlastmodified!).isBefore(DateTime.now()), isTrue);
expect(props.ocsize, 50598);
});

Expand Down Expand Up @@ -241,7 +242,7 @@ void main() {
expect(response.name, 'Nextcloud.png');
expect(response.isDirectory, isFalse);

expect(webdavDateFormat.parseUtc(response.props.davgetlastmodified!).isBefore(DateTime.now()), isTrue);
expect(parseHttpDate(response.props.davgetlastmodified!).isBefore(DateTime.now()), isTrue);
expect(response.props.davgetetag, isNotEmpty);
expect(response.props.davgetcontenttype, 'image/png');
expect(response.props.davgetcontentlength, 50598);
Expand Down Expand Up @@ -301,7 +302,7 @@ void main() {

expect(response.props.davgetcontenttype, isNull);
expect(
webdavDateFormat.parseUtc(response.props.davgetlastmodified!).millisecondsSinceEpoch,
parseHttpDate(response.props.davgetlastmodified!).millisecondsSinceEpoch,
closeTo(DateTime.now().millisecondsSinceEpoch, 10E3),
);
expect(response.props.davresourcetype!.collection, isNotNull);
Expand Down Expand Up @@ -370,7 +371,7 @@ void main() {
.first
.prop;
expect(props.ocfavorite, 1);
expect(webdavDateFormat.parseUtc(props.davgetlastmodified!), lastModifiedDate);
expect(parseHttpDate(props.davgetlastmodified!), lastModifiedDate);
expect(props.nccreationtime! * 1000, createdDate.millisecondsSinceEpoch);
expect(props.ncuploadtime! * 1000, closeTo(uploadTime.millisecondsSinceEpoch, 10E3));
});
Expand Down

0 comments on commit 9e99779

Please sign in to comment.