serve method
- HttpRequest request,
- Uri baseUri
Serves request; baseUri is the address of the fake.
Implementation
Future<void> serve(HttpRequest request, Uri baseUri) async {
final method = request.method.toUpperCase();
final path = request.uri.path;
final headers = <String, String>{};
request.headers.forEach(
(name, values) => headers[name.toLowerCase()] = values.join(','),
);
final received = BytesBuilder(copy: false);
await request.forEach(received.add);
final rawBytes = received.takeBytes();
// 12 3.2: a part is the raw bytes of `application/octet-stream`, never JSON.
final binary =
request.headers.contentType?.mimeType == 'application/octet-stream';
final raw = binary ? '' : utf8.decode(rawBytes, allowMalformed: true);
Object? body;
if (raw.isNotEmpty) {
try {
body = jsonDecode(raw);
} on FormatException {
await _send(
request,
'unknown',
method,
path,
null,
headers,
_problem(400, 'malformed_request'),
);
return;
}
}
_Route? route;
final params = <String, String>{};
for (final r in _routes) {
if (r.op.method != method) continue;
final m = r.pattern.firstMatch(path);
if (m == null) continue;
route = r;
for (var i = 0; i < r.names.length; i++) {
params[r.names[i]] = Uri.decodeComponent(m[i + 1]!);
}
break;
}
if (route == null) {
await _send(
request,
'unknown',
method,
path,
body,
headers,
_problem(404, 'not_found'),
);
return;
}
final op = route.op.operationId;
final handler = _handlers[op];
if (handler == null) {
await _send(
request,
op,
method,
path,
body,
headers,
_problem(501, 'internal_error', detail: 'not implemented by the fake'),
);
return;
}
final htu = '${baseUri.origin}$path';
FakeResponse response;
FakeSession? session;
String? jkt;
try {
(session, jkt) =
_ticketSession(op, headers, request.uri) ??
_authenticate(route.op, method, htu, headers);
final requestViolations = contract.request(op, body);
if (requestViolations.isNotEmpty) {
violations.add('request $op: ${requestViolations.join('; ')}');
throw FakeProblem(
400,
'validation_failed',
errors: [
for (final v in requestViolations)
{'pointer': v.pointer, 'code': v.code},
],
);
}
final fake = FakeRequest(
operationId: op,
method: method,
uri: request.uri,
pathParams: params,
headers: headers,
body: body,
world: world,
baseUri: baseUri,
session: session,
proofJkt: jkt,
bytes: binary ? rawBytes : null,
);
final fault = faults.take(op);
if (fault is AnswerFault) {
response = _problem(
fault.status,
fault.code,
reason: fault.reason,
retryAfter: fault.retryAfter,
);
} else {
response = await _idempotent(fake, handler);
if (fault is DelayFault) await Future<void>.delayed(fault.duration);
if (fault is DropResponseFault) {
requests.add(
FakeRequestRecord(
operationId: op,
method: method,
path: path,
status: 0,
body: body,
headers: headers,
),
);
final socket = await request.response.detachSocket(
writeHeaders: false,
);
socket.destroy();
return;
}
}
} on FakeProblem catch (p) {
response = _problem(
p.status,
p.code,
current: p.current,
reason: p.reason,
retryAfter: p.retryAfter,
errors: p.errors,
extra: p.extra,
);
} on _Unauthorized catch (u) {
response = u.response;
}
await _send(request, op, method, path, body, headers, response);
}