start method

Future<HttpServer> start()

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!;
}