add: working on request class port from symfony
This commit is contained in:
parent
b4d9009266
commit
f88021d0ce
7 changed files with 1590 additions and 3 deletions
|
@ -0,0 +1,18 @@
|
|||
import 'package:protevus_http/foundation_exception.dart';
|
||||
|
||||
/// Exception thrown when an HTTP request contains headers with conflicting information.
|
||||
///
|
||||
/// This exception is a subclass of [UnexpectedValueException] and implements
|
||||
/// [RequestExceptionInterface]. It is used to indicate that the headers in an
|
||||
/// HTTP request contain conflicting or inconsistent information.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// throw ConflictingHeadersException('Content-Type and Content-Encoding headers are incompatible');
|
||||
/// ```
|
||||
/// @author Magnus Nordlander <magnus@fervo.se>
|
||||
class ConflictingHeadersException extends UnexpectedValueException
|
||||
implements RequestExceptionInterface {
|
||||
/// Creates a new instance of [ConflictingHeadersException].
|
||||
ConflictingHeadersException([super.message]);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:protevus_http/foundation_exception.dart';
|
||||
|
||||
/// This exception is typically thrown when there's an issue with JSON parsing,
|
||||
/// serialization, or deserialization. It extends [UnexpectedValueException]
|
||||
/// and implements [RequestExceptionInterface].
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// try {
|
||||
/// // Some JSON operation
|
||||
/// } catch (e) {
|
||||
/// throw JsonException('Failed to parse JSON: $e');
|
||||
/// }
|
||||
/// ```
|
||||
/// @author Magnus Nordlander <magnus@fervo.se>
|
||||
class JsonException extends UnexpectedValueException
|
||||
implements RequestExceptionInterface {
|
||||
/// Creates a new instance of [JsonException].
|
||||
JsonException([super.message]);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import 'package:protevus_http/foundation_exception.dart';
|
||||
import 'package:protevus_mime/mime_exception.dart';
|
||||
|
||||
/// Raised when a session does not exist. This happens in the following cases:
|
||||
/// - the session is not enabled
|
||||
/// - attempt to read a session outside a request context (i.e., CLI script).
|
||||
class SessionNotFoundException extends LogicException
|
||||
implements RequestExceptionInterface {
|
||||
/// Creates a new [SessionNotFoundException] with the given [message], [code], and [previous] exception.
|
||||
SessionNotFoundException({
|
||||
String message = 'There is currently no session available.',
|
||||
int code = 0,
|
||||
Exception? previous,
|
||||
}) : super(message, code, previous);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import 'package:protevus_http/foundation_exception.dart';
|
||||
|
||||
/// Exception thrown when a suspicious operation is detected during an HTTP request.
|
||||
///
|
||||
/// This exception is a subclass of [UnexpectedValueException] and implements
|
||||
/// [RequestExceptionInterface]. It is used to indicate that a potentially
|
||||
/// unsafe or unexpected operation has been attempted or detected during
|
||||
/// the processing of an HTTP request.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// throw SuspiciousOperationException('Attempted to access restricted resource');
|
||||
/// ```
|
||||
/// @author Magnus Nordlander <magnus@fervo.se>
|
||||
class SuspiciousOperationException extends UnexpectedValueException
|
||||
implements RequestExceptionInterface {
|
||||
/// Creates a new instance of [ConflictingHeadersException].
|
||||
SuspiciousOperationException([super.message]);
|
||||
}
|
130
packages/http/lib/src/foundation/file_bag.dart
Normal file
130
packages/http/lib/src/foundation/file_bag.dart
Normal file
|
@ -0,0 +1,130 @@
|
|||
import 'dart:io';
|
||||
import 'dart:collection';
|
||||
import 'package:protevus_http/foundation.dart';
|
||||
import 'package:protevus_http/foundation_file.dart';
|
||||
import 'package:protevus_mime/mime_exception.dart';
|
||||
//import 'package:http/http.dart' as http;
|
||||
|
||||
/// FileBag is a container for uploaded files.
|
||||
///
|
||||
/// This class is ported from Symfony's HttpFoundation component.
|
||||
class FileBag extends ParameterBag
|
||||
with IterableMixin<MapEntry<String, dynamic>> {
|
||||
static const List<String> _fileKeys = [
|
||||
'error',
|
||||
'full_path',
|
||||
'name',
|
||||
'size',
|
||||
'tmp_name',
|
||||
'type'
|
||||
];
|
||||
|
||||
/// Constructs a FileBag instance.
|
||||
///
|
||||
/// [parameters] is an array of HTTP files.
|
||||
FileBag([Map<String, dynamic> parameters = const {}]) {
|
||||
replace(parameters);
|
||||
}
|
||||
|
||||
/// Replaces the current files with a new set.
|
||||
@override
|
||||
void replace(Map<String, dynamic> files) {
|
||||
parameters.clear();
|
||||
add(files);
|
||||
}
|
||||
|
||||
/// Sets a file in the bag.
|
||||
@override
|
||||
void set(String key, dynamic value) {
|
||||
if (value is! Map<String, dynamic> && value is! UploadedFile) {
|
||||
throw InvalidArgumentException(
|
||||
'An uploaded file must be a Map or an instance of UploadedFile.');
|
||||
}
|
||||
|
||||
super.set(key, _convertFileInformation(value));
|
||||
}
|
||||
|
||||
/// Adds multiple files to the bag.
|
||||
@override
|
||||
void add(Map<String, dynamic> files) {
|
||||
files.forEach((key, file) {
|
||||
set(key, file);
|
||||
});
|
||||
}
|
||||
|
||||
/// Converts uploaded files to UploadedFile instances.
|
||||
dynamic _convertFileInformation(dynamic file) {
|
||||
if (file is UploadedFile) {
|
||||
return file;
|
||||
}
|
||||
|
||||
if (file is Map<String, dynamic>) {
|
||||
file = _fixDartFilesMap(file);
|
||||
List<String> keys = (file.keys.toList()..add('full_path'))..sort();
|
||||
|
||||
if (listEquals(_fileKeys, keys)) {
|
||||
if (file['error'] == HttpStatus.noContent) {
|
||||
return null;
|
||||
} else {
|
||||
return UploadedFile(
|
||||
file['tmp_name'],
|
||||
file['full_path'] ?? file['name'],
|
||||
file['type'],
|
||||
file['error'],
|
||||
false,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return file.map((key, value) {
|
||||
if (value is UploadedFile || value is Map<String, dynamic>) {
|
||||
return MapEntry(key, _convertFileInformation(value));
|
||||
}
|
||||
return MapEntry(key, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/// Fixes a malformed Dart file upload map.
|
||||
///
|
||||
/// This method is equivalent to PHP's fixPhpFilesArray.
|
||||
Map<String, dynamic> _fixDartFilesMap(Map<String, dynamic> data) {
|
||||
List<String> keys = (data.keys.toList()..add('full_path'))..sort();
|
||||
|
||||
if (!listEquals(_fileKeys, keys) ||
|
||||
!data.containsKey('name') ||
|
||||
data['name'] is! Map) {
|
||||
return data;
|
||||
}
|
||||
|
||||
Map<String, dynamic> files = Map.from(data);
|
||||
for (String k in _fileKeys) {
|
||||
files.remove(k);
|
||||
}
|
||||
|
||||
(data['name'] as Map).forEach((key, name) {
|
||||
files[key] = _fixDartFilesMap({
|
||||
'error': data['error'][key],
|
||||
'name': name,
|
||||
'type': data['type'][key],
|
||||
'tmp_name': data['tmp_name'][key],
|
||||
'size': data['size'][key],
|
||||
if (data.containsKey('full_path') && data['full_path'].containsKey(key))
|
||||
'full_path': data['full_path'][key],
|
||||
});
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility function to compare two lists for equality.
|
||||
bool listEquals<T>(List<T> a, List<T> b) {
|
||||
if (a.length != b.length) return false;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (a[i] != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
1333
packages/http/lib/src/foundation/request.dart
Normal file
1333
packages/http/lib/src/foundation/request.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,57 @@
|
|||
import 'package:protevus_mime/src/exception/exception_interface.dart';
|
||||
class LogicException implements Exception {
|
||||
final String message;
|
||||
final int code;
|
||||
final Exception? previous;
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
class LogicException extends StateError implements ExceptionInterface {
|
||||
LogicException(super.message);
|
||||
LogicException(
|
||||
[this.message = "", this.code = 0, this.previous, this.stackTrace]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "LogicException: $message";
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
Exception? getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
String getFile() {
|
||||
final frames = stackTrace?.toString().split('\n');
|
||||
if (frames != null && frames.isNotEmpty) {
|
||||
final frame = frames.first;
|
||||
final fileInfo = frame.split(' ').last;
|
||||
return fileInfo.split(':').first;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int getLine() {
|
||||
final frames = stackTrace?.toString().split('\n');
|
||||
if (frames != null && frames.isNotEmpty) {
|
||||
final frame = frames.first;
|
||||
final fileInfo = frame.split(' ').last;
|
||||
final lineInfo = fileInfo.split(':');
|
||||
if (lineInfo.length > 1) {
|
||||
return int.tryParse(lineInfo[1]) ?? 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<String> getTrace() {
|
||||
return stackTrace?.toString().split('\n') ?? [];
|
||||
}
|
||||
|
||||
String getTraceAsString() {
|
||||
return stackTrace?.toString() ?? "";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue