startSecureServer method

Future<List<HttpServer>> startSecureServer(
  1. Handler handler,
  2. Map<String, String> domainsAndEmails, {
  3. int port = 80,
  4. int securePort = 443,
  5. dynamic bindingAddress = '0.0.0.0',
  6. int? backlog,
  7. bool shared = false,
  8. bool checkCertificate = true,
  9. bool requestCertificate = true,
  10. bool forceRequestCertificate = false,
  11. bool loadAllHandledDomains = false,
})

Starts 2 HttpServer instances, one HTTP at port and other HTTPS at securePort.

  • If checkCertificate is true will check the current certificate.

Implementation

Future<List<HttpServer>> startSecureServer(
    Handler handler, Map<String, String> domainsAndEmails,
    {int port = 80,
    int securePort = 443,
    bindingAddress = '0.0.0.0',
    int? backlog,
    bool shared = false,
    bool checkCertificate = true,
    bool requestCertificate = true,
    bool forceRequestCertificate = false,
    bool loadAllHandledDomains = false}) async {
  _logInfo(
      'Starting server> bindingAddress: $bindingAddress ; port: $port ; domainAndEmails: $domainsAndEmails');

  FutureOr<Response> handlerWithChallenge(r) {
    final path = r.requestedUri.path;

    if (LetsEncrypt.isWellKnownPath(path)) {
      if (LetsEncrypt.isACMEPath(path)) {
        return processACMEChallengeRequest(r);
      } else if (LetsEncrypt.isSelfCheckPath(path)) {
        return processSelfCheckRequest(r);
      }
    }

    return handler(r);
  }

  var server = await serve(handlerWithChallenge, bindingAddress, port,
      backlog: backlog, shared: shared);

  Future<HttpServer> startSecureServer(SecurityContext securityContext) {
    return serve(handlerWithChallenge, bindingAddress, securePort,
        securityContext: securityContext, backlog: backlog, shared: shared);
  }

  HttpServer? secureServer;

  var domains = domainsAndEmails.keys.toList();

  _logInfo('$certificatesHandler');
  _logInfo('Handled domains: ${certificatesHandler.listAllHandledDomains()}');

  var securityContext = await certificatesHandler.buildSecurityContext(
      domains,
      loadAllHandledDomains: loadAllHandledDomains);

  _logInfo(
      'securityContext[loadAllHandledDomains: $loadAllHandledDomains]: $securityContext');

  if (securityContext == null) {
    if (!requestCertificate) {
      throw StateError(
          "No previous SecureContext. Parameter `requestCertificate` is `false`, can't request certificate!");
    }

    var domainsToCheck = certificatesHandler.listNotHandledDomains(domains);

    _logInfo('Requesting certificate for: $domainsToCheck');

    for (var domain in domainsToCheck) {
      var domainEmail = domainsAndEmails[domain]!;
      var ok = await this.requestCertificate(domain, domainEmail);
      if (!ok) {
        throw StateError("Error requesting certificate!");
      }
    }

    securityContext = await certificatesHandler.buildSecurityContext(domains,
        loadAllHandledDomains: loadAllHandledDomains);
    if (securityContext == null) {
      throw StateError(
          "Error loading SecureContext after successful request of certificate for: $domains");
    }

    _logInfo('Starting secure server> port: $securePort ; domains: $domains');
    secureServer = await startSecureServer(securityContext);
  } else {
    secureServer = await startSecureServer(securityContext);

    if (checkCertificate) {
      _logInfo('Checking domains certificates: $domains');

      var refreshedCertificate = false;

      for (var domain in domains) {
        var domainEmail = domainsAndEmails[domain]!;

        _logInfo('Checking certificate for: $domain');

        var checkCertificateStatus = await this.checkCertificate(
            domain, domainEmail,
            requestCertificate: requestCertificate,
            forceRequestCertificate: forceRequestCertificate);

        _logInfo('CheckCertificateStatus: $checkCertificateStatus');

        if (checkCertificateStatus.isOkRefreshed) {
          refreshedCertificate = true;
        } else if (checkCertificateStatus.isNotOK) {
          throw StateError(
              "Certificate check error! Status: $checkCertificateStatus ; domain: $domain");
        }
      }

      if (refreshedCertificate) {
        _logWarning('Refreshing SecureContext due new certificate.');
        securityContext = await certificatesHandler.buildSecurityContext(
            domains,
            loadAllHandledDomains: loadAllHandledDomains);
        if (securityContext == null) {
          throw StateError(
              "Error loading SecureContext after successful certificate check for: $domains");
        }

        _logWarning('Restarting secure server...');
        await secureServer.close(force: true);
        secureServer = await startSecureServer(securityContext);
      }
    }
  }

  return [server, secureServer];
}