public IDisposable CreateServer(ServerAddress address)
        {
            var listeners = new List <IAsyncDisposable>();

            var usingPipes = address.IsUnixPipe;

            try
            {
                var pipeName = (Libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n");

                var single = Threads.Count == 1;
                var first  = true;

                foreach (var thread in Threads)
                {
                    if (single)
                    {
                        var listener = usingPipes ?
                                       (Listener) new PipeListener(this) :
                                       new TcpListener(this);
                        listeners.Add(listener);
                        listener.StartAsync(address, thread).Wait();
                    }
                    else if (first)
                    {
                        var listener = usingPipes
                            ? (ListenerPrimary) new PipeListenerPrimary(this)
                            : new TcpListenerPrimary(this);

                        listeners.Add(listener);
                        listener.StartAsync(pipeName, address, thread).Wait();
                    }
                    else
                    {
                        var listener = usingPipes
                            ? (ListenerSecondary) new PipeListenerSecondary(this)
                            : new TcpListenerSecondary(this);
                        listeners.Add(listener);
                        listener.StartAsync(pipeName, address, thread).Wait();
                    }

                    first = false;
                }

                return(new Disposable(() =>
                {
                    DisposeListeners(listeners);
                }));
            }
            catch
            {
                DisposeListeners(listeners);

                throw;
            }
        }
Exemplo n.º 2
0
        public void Start <TContext>(IHttpApplication <TContext> application)
        {
            if (_disposables != null)
            {
                // The server has already started and/or has not been cleaned up yet
                throw new InvalidOperationException("Server has already started.");
            }
            _disposables = new Stack <IDisposable>();

            try
            {
                var dateHeaderValueManager = new DateHeaderValueManager();
                var trace  = new KestrelTrace(_logger);
                var engine = new KestrelEngine(new ServiceContext
                {
                    FrameFactory = context =>
                    {
                        return(new Frame <TContext>(application, context));
                    },
                    AppLifetime            = _applicationLifetime,
                    Log                    = trace,
                    ThreadPool             = new LoggingThreadPool(trace),
                    DateHeaderValueManager = dateHeaderValueManager,
                    ServerOptions          = Options
                });

                _disposables.Push(engine);
                _disposables.Push(dateHeaderValueManager);

                var threadCount = Options.ThreadCount;

                if (threadCount <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(threadCount),
                                                          threadCount,
                                                          "ThreadCount must be positive.");
                }

                if (!Constants.ECONNRESET.HasValue)
                {
                    _logger.LogWarning("Unable to determine ECONNRESET value on this platform.");
                }
                if (!Constants.EADDRINUSE.HasValue)
                {
                    _logger.LogWarning("Unable to determine EADDRINUSE value on this platform.");
                }

                engine.Start(threadCount);
                var atLeastOneListener = false;

                foreach (var address in _serverAddresses.Addresses.ToArray())
                {
                    var parsedAddress = ServerAddress.FromUrl(address);
                    atLeastOneListener = true;

                    if (!parsedAddress.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
                    {
                        _disposables.Push(engine.CreateServer(
                                              parsedAddress));
                    }
                    else
                    {
                        if (parsedAddress.Port == 0)
                        {
                            throw new InvalidOperationException("Dynamic port binding is not supported when binding to localhost. You must either bind to 127.0.0.1:0 or [::1]:0, or both.");
                        }

                        var ipv4Address = parsedAddress.WithHost("127.0.0.1");
                        var exceptions  = new List <UvException>();

                        try
                        {
                            _disposables.Push(engine.CreateServer(ipv4Address));
                        }
                        catch (AggregateException ex)
                        {
                            var uvException = ex.InnerException as UvException;

                            if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE)
                            {
                                _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv4 loopback interface.");
                                exceptions.Add(uvException);
                            }
                            else
                            {
                                throw;
                            }
                        }

                        var ipv6Address = parsedAddress.WithHost("[::1]");

                        try
                        {
                            _disposables.Push(engine.CreateServer(ipv6Address));
                        }
                        catch (AggregateException ex)
                        {
                            var uvException = ex.InnerException as UvException;

                            if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE)
                            {
                                _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv6 loopback interface.");
                                exceptions.Add(uvException);
                            }
                            else
                            {
                                throw;
                            }
                        }

                        if (exceptions.Count == 2)
                        {
                            var ex = new AggregateException(exceptions);
                            _logger.LogError(0, ex, $"Unable to bind to {parsedAddress.ToString()} on any loopback interface.");
                            throw ex;
                        }
                    }

                    // If requested port was "0", replace with assigned dynamic port.
                    _serverAddresses.Addresses.Remove(address);
                    _serverAddresses.Addresses.Add(parsedAddress.ToString());
                }

                if (!atLeastOneListener)
                {
                    throw new InvalidOperationException("No recognized listening addresses were configured.");
                }
            }
            catch
            {
                Dispose();
                throw;
            }
        }
        public void Start <TContext>(IHttpApplication <TContext> application)
        {
            if (_disposables != null)
            {
                // The server has already started and/or has not been cleaned up yet
                throw new InvalidOperationException("Server has already started.");
            }
            _disposables = new Stack <IDisposable>();

            try
            {
                var componentFactory       = Features.Get <IHttpComponentFactory>();
                var dateHeaderValueManager = new DateHeaderValueManager();
                var trace  = new KestrelTrace(_logger);
                var engine = new KestrelEngine(new ServiceContext
                {
                    FrameFactory = context =>
                    {
                        return(new Frame <TContext>(application, context));
                    },
                    AppLifetime            = _applicationLifetime,
                    Log                    = trace,
                    ThreadPool             = new LoggingThreadPool(trace),
                    DateHeaderValueManager = dateHeaderValueManager,
                    ServerOptions          = Options,
                    HttpComponentFactory   = componentFactory
                });

                _disposables.Push(engine);
                _disposables.Push(dateHeaderValueManager);

                var threadCount = Options.ThreadCount;

                if (threadCount <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(threadCount),
                                                          threadCount,
                                                          "ThreadCount must be positive.");
                }

                engine.Start(threadCount);
                var atLeastOneListener = false;

                foreach (var address in _serverAddresses.Addresses)
                {
                    var parsedAddress = ServerAddress.FromUrl(address);
                    if (parsedAddress == null)
                    {
                        throw new FormatException("Unrecognized listening address: " + address);
                    }
                    else
                    {
                        atLeastOneListener = true;
                        _disposables.Push(engine.CreateServer(
                                              parsedAddress));
                    }
                }

                if (!atLeastOneListener)
                {
                    throw new InvalidOperationException("No recognized listening addresses were configured.");
                }
            }
            catch
            {
                Dispose();
                throw;
            }
        }
        public static ServerAddress FromUrl(string url)
        {
            url = url ?? string.Empty;

            int schemeDelimiterStart = url.IndexOf("://", StringComparison.Ordinal);

            if (schemeDelimiterStart < 0)
            {
                int port;
                if (int.TryParse(url, NumberStyles.None, CultureInfo.InvariantCulture, out port))
                {
                    return(new ServerAddress()
                    {
                        Scheme = "http",
                        Host = "+",
                        Port = port,
                        PathBase = "/"
                    });
                }
                return(null);
            }
            int schemeDelimiterEnd = schemeDelimiterStart + "://".Length;

            var isUnixPipe = url.IndexOf(Constants.UnixPipeHostPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd;

            int pathDelimiterStart;
            int pathDelimiterEnd;

            if (!isUnixPipe)
            {
                pathDelimiterStart = url.IndexOf("/", schemeDelimiterEnd, StringComparison.Ordinal);
                pathDelimiterEnd   = pathDelimiterStart;
            }
            else
            {
                pathDelimiterStart = url.IndexOf(":", schemeDelimiterEnd + Constants.UnixPipeHostPrefix.Length, StringComparison.Ordinal);
                pathDelimiterEnd   = pathDelimiterStart + ":".Length;
            }

            if (pathDelimiterStart < 0)
            {
                pathDelimiterStart = pathDelimiterEnd = url.Length;
            }

            var serverAddress = new ServerAddress();

            serverAddress.Scheme = url.Substring(0, schemeDelimiterStart);

            var hasSpecifiedPort = false;

            if (!isUnixPipe)
            {
                int portDelimiterStart = url.LastIndexOf(":", pathDelimiterStart - 1, pathDelimiterStart - schemeDelimiterEnd, StringComparison.Ordinal);
                if (portDelimiterStart >= 0)
                {
                    int portDelimiterEnd = portDelimiterStart + ":".Length;

                    string portString = url.Substring(portDelimiterEnd, pathDelimiterStart - portDelimiterEnd);
                    int    portNumber;
                    if (int.TryParse(portString, NumberStyles.Integer, CultureInfo.InvariantCulture, out portNumber))
                    {
                        hasSpecifiedPort   = true;
                        serverAddress.Host = url.Substring(schemeDelimiterEnd, portDelimiterStart - schemeDelimiterEnd);
                        serverAddress.Port = portNumber;
                    }
                }

                if (!hasSpecifiedPort)
                {
                    if (string.Equals(serverAddress.Scheme, "http", StringComparison.OrdinalIgnoreCase))
                    {
                        serverAddress.Port = 80;
                    }
                    else if (string.Equals(serverAddress.Scheme, "https", StringComparison.OrdinalIgnoreCase))
                    {
                        serverAddress.Port = 443;
                    }
                }
            }

            if (!hasSpecifiedPort)
            {
                serverAddress.Host = url.Substring(schemeDelimiterEnd, pathDelimiterStart - schemeDelimiterEnd);
            }

            // Path should not end with a / since it will be used as PathBase later
            if (url[url.Length - 1] == '/')
            {
                serverAddress.PathBase = url.Substring(pathDelimiterEnd, url.Length - pathDelimiterEnd - 1);
            }
            else
            {
                serverAddress.PathBase = url.Substring(pathDelimiterEnd);
            }

            serverAddress.PathBase = PathNormalizer.NormalizeToNFC(serverAddress.PathBase);

            return(serverAddress);
        }