start method
Starts the HttpServer.
Implementation
Future<HttpServer> start() async {
// Create a router instance with the default not found handler.
final router = shelf_router.Router(
notFoundHandler: _notFoundHandler,
);
// TODO move the controller logic elsewhere
// Create a new route for each controller.
for (final controller in controllers) {
shelf.Pipeline controllerPipeline = shelf.Pipeline();
controllerPipeline = createMiddlewarePipeline(
controllerPipeline,
this,
controller.middleware,
);
if (controller is TargetProxyController) {
// TODO: Add path validation (e.g. cannot be regex)
router.mount(
controller.path,
controllerPipeline.addHandler((shelf.Request shelfRequest) async {
Request request = createRequest(
this,
shelfRequest,
);
var res;
final response = await controller.proxy(request);
if (response is ResponseProxy) {
final proxy = shelf_proxy.proxyHandler(
response.uri,
proxyName: controller.name,
);
res = await proxy(request.delegate);
} else {
res = response.delegate;
}
request.splitter.close();
return res;
}),
);
break;
}
if (controller is TargetController) {
router.all(controller.path,
controllerPipeline.addHandler((shelf.Request shelfRequest) async {
// Create a Target request from the Shelf request.
Request request = createRequest(
this,
shelfRequest,
);
// Handle the Target request to a Shelf request
Future<shelf.Response> toShelfResponse(
FutureOr<Response> response,
) async {
try {
Response handledResponse = await response;
_assertNotResponseProxy(handledResponse);
return handledResponse.delegate;
} on RequestNotHandled {
try {
Response handledResponse = await await controller.any(request);
_assertNotResponseProxy(handledResponse);
return handledResponse.delegate;
} on RequestNotHandled {
return await _notFoundHandler(shelfRequest);
} catch (error, stackTrace) {
return await _serverErrorHandler(
shelfRequest,
error,
stackTrace,
);
}
} catch (error, stackTrace) {
return await _serverErrorHandler(shelfRequest, error, stackTrace);
} finally {
request.splitter.close();
}
}
// A HEAD handler should always be implemeted if a GET handler is.
// This function first tries to call the user defined HEAD handler,
// or if it doesn't exist, then calls the GET handler but removes the
// body from the response.
Future<shelf.Response> handleHeadRequest(Request request) async {
try {
// First try to return a user implemented HEAD request.
Response handledResponse = await controller.head(request);
_assertNotResponseProxy(handledResponse);
return handledResponse.delegate;
} on RequestNotHandled {
try {
// If not implemented, try to call the GET handler
final handledResponse = await controller.get(request);
_assertNotResponseProxy(handledResponse);
return _removeResponseBody(handledResponse.delegate);
} on RequestNotHandled {
// If no GET handler, throw a 404 as expected.
return await _notFoundHandler(shelfRequest);
} catch (error, stackTrace) {
return await _serverErrorHandler(
shelfRequest,
error,
stackTrace,
);
}
} catch (error, stackTrace) {
return await _serverErrorHandler(
shelfRequest,
error,
stackTrace,
);
} finally {
request.splitter.close();
}
}
try {
final wsHandshake = shelfRequest.createWebSocketHandshake(
configuration.ws,
);
// Only create a WebSocket handler if the handshake was a success.
if (wsHandshake != null) {
shelfRequest.createWebSocketHandler(wsHandshake, (handler) async {
try {
final stream = controller.ws(request, handler);
await handler.delegate.sink.addStream(stream);
handler.close(
ws_status.normalClosure,
'The server closed the WebSocket handler.',
);
} on RequestNotHandled {
handler.close(
ws_status.normalClosure,
'The server has no valid WebSocket handler.',
);
} catch (_) {
// TODO(ehesp): Do something with the error (log it, event etc).
handler.close(ws_status.internalServerError);
}
});
}
} on WebSocketHandshakeAborted catch (e) {
return shelf.Response.forbidden(e.reason);
}
switch (request.method) {
case HttpMethod.connect:
return toShelfResponse(controller.connect(request));
case HttpMethod.delete:
return toShelfResponse(controller.delete(request));
case HttpMethod.get:
return toShelfResponse(controller.get(request));
case HttpMethod.head:
return handleHeadRequest(request);
case HttpMethod.options:
return toShelfResponse(controller.options(request));
case HttpMethod.patch:
return toShelfResponse(controller.patch(request));
case HttpMethod.post:
return toShelfResponse(controller.post(request));
case HttpMethod.put:
return toShelfResponse(controller.put(request));
default:
return _notFoundHandler(shelfRequest);
}
}));
}
}
final pipeline = _createServerPipeline();
final staticHandler = _createStaticFileHandler();
final routerHandler = pipeline.addHandler(router);
shelf.Cascade cascade = shelf.Cascade();
// First ensure static files are served before anything else
if (staticHandler != null) {
cascade = cascade.add(staticHandler);
}
// Add the controller route handler
cascade = cascade.add(routerHandler);
/// Create the HTTP server & start listening.
server = await shelf_io.serve(
cascade.handler, configuration.http.host, configuration.http.port);
server!.autoCompress = configuration.http.compressed;
return server!;
}