// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:async/async.dart';

import 'src/multi_headers.dart';
import 'src/utils.dart';

/// The error code for an error caused by a port already being in use.
final _addressInUseErrno = _computeAddressInUseErrno();
int _computeAddressInUseErrno() {
  if (Platform.isWindows) return 10048;
  if (Platform.isMacOS) return 48;
  assert(Platform.isLinux);
  return 98;
}

/// An implementation of `dart:io`'s [HttpServer] that wraps multiple servers
/// and forwards methods to all of them.
///
/// This is useful for serving the same application on multiple network
/// interfaces while still having a unified way of controlling the servers. In
/// particular, it supports serving on both the IPv4 and IPv6 loopback addresses
/// using [HttpMultiServer.loopback].
class HttpMultiServer extends StreamView<HttpRequest> implements HttpServer {
  /// The wrapped servers.
  final Set<HttpServer> _servers;

  /// Returns the default value of the `Server` header for all responses
  /// generated by each server.
  ///
  /// If the wrapped servers have different default values, it's not defined
  /// which value is returned.
  String get serverHeader => _servers.first.serverHeader;
  set serverHeader(String value) {
    for (var server in _servers) {
      server.serverHeader = value;
    }
  }

  /// Returns the default set of headers added to all response objects.
  ///
  /// If the wrapped servers have different default headers, it's not defined
  /// which header is returned for accessor methods.
  final HttpHeaders defaultResponseHeaders;

  Duration get idleTimeout => _servers.first.idleTimeout;
  set idleTimeout(Duration value) {
    for (var server in _servers) {
      server.idleTimeout = value;
    }
  }

  bool get autoCompress => _servers.first.autoCompress;
  set autoCompress(bool value) {
    for (var server in _servers) {
      server.autoCompress = value;
    }
  }

  /// Returns the port that one of the wrapped servers is listening on.
  ///
  /// If the wrapped servers are listening on different ports, it's not defined
  /// which port is returned.
  int get port => _servers.first.port;

  /// Returns the address that one of the wrapped servers is listening on.
  ///
  /// If the wrapped servers are listening on different addresses, it's not
  /// defined which address is returned.
  InternetAddress get address => _servers.first.address;

  set sessionTimeout(int value) {
    for (var server in _servers) {
      server.sessionTimeout = value;
    }
  }

  /// Creates an [HttpMultiServer] wrapping [servers].
  ///
  /// All [servers] should have the same configuration and none should be
  /// listened to when this is called.
  HttpMultiServer(Iterable<HttpServer> servers)
      : _servers = servers.toSet(),
        defaultResponseHeaders = MultiHeaders(
            servers.map((server) => server.defaultResponseHeaders)),
        super(StreamGroup.merge(servers));

  /// Creates an [HttpServer] listening on all available loopback addresses for
  /// this computer.
  ///
  /// See [HttpServer.bind].
  static Future<HttpServer> loopback(int port,
      {int backlog, bool v6Only = false, bool shared = false}) {
    if (backlog == null) backlog = 0;

    return _loopback(
        port,
        (address, port) => HttpServer.bind(address, port,
            backlog: backlog, v6Only: v6Only, shared: shared));
  }

  /// Like [loopback], but supports HTTPS requests.
  ///
  /// See [HttpServer.bindSecure].
  static Future<HttpServer> loopbackSecure(int port, SecurityContext context,
      {int backlog,
      bool v6Only = false,
      bool requestClientCertificate = false,
      bool shared = false}) {
    if (backlog == null) backlog = 0;

    return _loopback(
        port,
        (address, port) => HttpServer.bindSecure(address, port, context,
            backlog: backlog,
            v6Only: v6Only,
            shared: shared,
            requestClientCertificate: requestClientCertificate));
  }

  /// A helper method for initializing loopback servers.
  ///
  /// [bind] should forward to either [HttpServer.bind] or
  /// [HttpServer.bindSecure].
  static Future<HttpServer> _loopback(
      int port, Future<HttpServer> bind(InternetAddress address, int port),
      [int remainingRetries]) async {
    remainingRetries ??= 5;

    if (!await supportsIPv4) {
      return await bind(InternetAddress.loopbackIPv6, port);
    }

    var v4Server = await bind(InternetAddress.loopbackIPv4, port);
    if (!await supportsIPv6) return v4Server;

    try {
      // Reuse the IPv4 server's port so that if [port] is 0, both servers use
      // the same ephemeral port.
      var v6Server = await bind(InternetAddress.loopbackIPv6, v4Server.port);
      return HttpMultiServer([v4Server, v6Server]);
    } on SocketException catch (error) {
      // If there is already a server listening we'll lose the reference on a
      // rethrow.
      await v4Server.close();

      if (error.osError.errorCode != _addressInUseErrno) rethrow;
      if (port != 0) rethrow;
      if (remainingRetries == 0) rethrow;

      // A port being available on IPv4 doesn't necessarily mean that the same
      // port is available on IPv6. If it's not (which is rare in practice),
      // we try again until we find one that's available on both.
      return await _loopback(port, bind, remainingRetries - 1);
    }
  }

  Future close({bool force = false}) =>
      Future.wait(_servers.map((server) => server.close(force: force)));

  /// Returns an HttpConnectionsInfo object summarizing the total number of
  /// current connections handled by all the servers.
  HttpConnectionsInfo connectionsInfo() {
    var info = HttpConnectionsInfo();
    for (var server in _servers) {
      var subInfo = server.connectionsInfo();
      info.total += subInfo.total;
      info.active += subInfo.active;
      info.idle += subInfo.idle;
      info.closing += subInfo.closing;
    }
    return info;
  }
}
