public static void Attach(
            IApplicationBuilder appBuilder,
            string sourcePath,
            string npmScriptName)
        {
            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
            }

            // Start Angular CLI and attach to middleware pipeline
            var logger = GetOrCreateLogger(appBuilder);
            var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, logger);

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the Angular CLI middleware server)
            // - given that, there's no reason to use https, and we couldn't even if we
            //   wanted to, because in general the Angular CLI server has no certificate
            var targetUriTask = angularCliServerInfoTask.ContinueWith(
                task => new UriBuilder("http", "localhost", task.Result.Port).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(appBuilder, targetUriTask);
        }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
            }

            // Start Vue CLI and attach to middleware pipeline
            var appBuilder           = spaBuilder.ApplicationBuilder;
            var logger               = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var vueCliServerInfoTask = StartVueCliServerAsync(sourcePath, npmScriptName, logger);

            var targetUriTask = vueCliServerInfoTask.ContinueWith(
                task => new UriBuilder(task.Result.Scheme, task.Result.Host, task.Result.Port).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The Vue CLI process did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
Example #3
0
        public static void Attach(ISpaBuilder spaBuilder, string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
            }

            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var portTask   = StartVueDevServerAsync(sourcePath, npmScriptName, logger);

            var targetUriTask = portTask.ContinueWith(
                task => new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The vue development server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
        private static TimeSpan StartupTimeout    = TimeSpan.FromSeconds(50); // Note that the HTTP request itself by default times out after 60s, so you only get useful error information if this is shorter

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
            }

            // Start create-react-app and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var portTask   = StartCreateReactAppServerAsync(sourcePath, npmScriptName, logger);

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the create-react-app server)
            // - given that, there's no reason to use https, and we couldn't even if we
            //   wanted to, because in general the create-react-app server has no certificate
            var targetUriTask = portTask.ContinueWith(
                task => new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, targetUriTask);
        }
Example #5
0
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromMinutes(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string scriptName, int port = 8080, ScriptRunnerType runner = ScriptRunnerType.Npm, string regex = DefaultRegex, bool forceKill = false, bool useProxy = true)
        {
            string sourcePath = spaBuilder.Options.SourcePath;

            Console.WriteLine("sourcePath", sourcePath);
            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(scriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
            }

            // Start vue-cli and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var portTask   = StartSpaCliServerAsync(sourcePath, scriptName, logger, port, runner, regex, forceKill);

            if (!useProxy)
            {
                return;
            }

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the vue-cli server)
            // - given that, there's no reason to use https, and we couldn't even if we
            //   wanted to, because in general the vue-cli server has no certificate
            var targetUriTask = portTask.ContinueWith(
                task => new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The svelte-cli server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
        /// <summary>
        /// Builder and script are expected to be non-null, as parameters are expected to be checked by the calling function.
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="script"></param>
        public static void Attach(ISpaBuilder builder, string script)
        {
            string sourcePath = builder.Options.SourcePath;

            IApplicationBuilder appBuilder = builder.ApplicationBuilder;
            ILogger             logger     = GetOrCreateLogger(appBuilder, LogCategoryName);
            Task <int>          portTask   = StartPreactServerAsync(sourcePath, script, logger);

            Task <Uri> targetUriTask = portTask.ContinueWith(task => new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(builder, () =>
            {
                TimeSpan timeout = builder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The preact development server didn't start within {timeout.Seconds} s.\nCheck the log output for " +
                                                 $"more information"));
            });
        }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string scriptName)
        {
            var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand;
            var sourcePath        = spaBuilder.Options.SourcePath;
            var devServerPort     = spaBuilder.Options.DevServerPort;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(scriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
            }

            // Start create-react-app and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService <IHostApplicationLifetime>().ApplicationStopping;
            var logger           = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var diagnosticSource = appBuilder.ApplicationServices.GetRequiredService <DiagnosticSource>();
            var portTask         = StartCreateReactAppServerAsync(sourcePath, scriptName, pkgManagerCommand, devServerPort, logger, diagnosticSource, applicationStoppingToken);

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the create-react-app server)
            // - given that, there's no reason to use https, and we couldn't even if we
            //   wanted to, because in general the create-react-app server has no certificate
            var targetUriTask = portTask.ContinueWith(
                task => new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The create-react-app server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
Example #8
0
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
            }

            // Start Vue CLI and attach to middleware pipeline
            var appBuilder           = spaBuilder.ApplicationBuilder;
            var logger               = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var VueCliServerInfoTask = StartVueCliServerAsync(sourcePath, npmScriptName, logger);

            // Everything we proxy is hardcoded to target http://localhost because:
            // - the requests are always from the local machine (we're not accepting remote
            //   requests that go directly to the Vue CLI middleware server)
            // - given that, there's no reason to use https, and we couldn't even if we
            //   wanted to, because in general the Vue CLI server has no certificate
            var targetUriTask = VueCliServerInfoTask.ContinueWith(
                task =>
            {
                return(new UriBuilder("http", "localhost", task.Result.Port).Uri);
            });

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The Vue CLI process did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromMinutes(5); // 这是仅开发时间的功能, 因此很长时间的超时是可以的

        public static void Attach(
            ISpaBuilder spaBuilder,
            string scriptName, int port = 0, ScriptRunnerType runner = ScriptRunnerType.Npm, string regex = DefaultRegex)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
            }

            if (string.IsNullOrEmpty(scriptName))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
            }

            // 启动vue-cli并连接到中间件管道
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
            var portTask   = StartVueCliServerAsync(sourcePath, scriptName, logger, port, runner, regex);

            // 我们代理的所有内容都硬编码为目标http://localhost,因为:
            // - 请求总是来自本地机器(我们不接受远程
            //   直接转到vue-cli服务器的请求)
            // - 鉴于此,没有理由使用https,即使我们也不行
            //   想要,因为通常vue-cli服务器没有证书
            var targetUriTask = portTask.ContinueWith(
                task =>
                new UriBuilder("http", "localhost", task.Result).Uri);

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // 在每个请求中,我们创建一个具有自己超时的单独启动任务。 就这样,即使
                // 第一个请求超时,后续请求仍然有效。
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The vue-cli server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
Example #10
0
    private static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

    public static void Attach(
        ISpaBuilder spaBuilder,
        string scriptName)
    {
        var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand;
        var sourcePath        = spaBuilder.Options.SourcePath;
        var devServerPort     = spaBuilder.Options.DevServerPort;

        if (string.IsNullOrEmpty(sourcePath))
        {
            throw new ArgumentException("Property 'SourcePath' cannot be null or empty", nameof(spaBuilder));
        }

        if (string.IsNullOrEmpty(scriptName))
        {
            throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
        }

        // Start Angular CLI and attach to middleware pipeline
        var appBuilder = spaBuilder.ApplicationBuilder;
        var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService <IHostApplicationLifetime>().ApplicationStopping;
        var logger                   = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
        var diagnosticSource         = appBuilder.ApplicationServices.GetRequiredService <DiagnosticSource>();
        var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, scriptName, pkgManagerCommand, devServerPort, logger, diagnosticSource, applicationStoppingToken);

        SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
        {
            // On each request, we create a separate startup task with its own timeout. That way, even if
            // the first request times out, subsequent requests could still work.
            var timeout = spaBuilder.Options.StartupTimeout;
            return(angularCliServerInfoTask.WithTimeout(timeout,
                                                        $"The Angular CLI process did not start listening for requests " +
                                                        $"within the timeout period of {timeout.TotalSeconds} seconds. " +
                                                        $"Check the log output for error information."));
        });
    }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new InvalidOperationException("Must set ISpaBuilder.Options.SourcePath before calling this method.");
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            // Start webpack-dev-server and attach to middleware pipeline
            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);

            Task <int> portTask      = null;
            Task <Uri> targetUriTask = null;
            appBuilder.Use(async(context, next) =>
            {
                if (portTask == null)
                {
                    // Get port number of webapp first before we start webpack-dev-server, so that
                    // webpack can use the port number of the webapp for the websocket configuration.
                    var request          = context.Request;
                    int socketPortNumber = request.Host.Port.Value;
                    portTask             = StartWebpackDevServerAsync(sourcePath, npmScriptName, logger, socketPortNumber);
                    // Everything we **proxy** is hardcoded to target http://localhost because:
                    // - the requests are always from the local machine (we're not accepting remote
                    //   requests that go directly to the webpack-dev-server server)
                    // - given that, there's no reason to use https, and we couldn't even if we
                    //   wanted to, because in general the webpack-dev-server server has no certificate
#pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler
                    targetUriTask = portTask.ContinueWith(task =>
                    {
                        // "https" here doesn't work as the webpack-dev-server expects request via "http"
                        Uri uri = new UriBuilder("http", "localhost", task.Result).Uri;
                        return(uri);
                    });
#pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler
                }

#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
                await next.Invoke();
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
            });

            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The webpack-dev-server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }
        private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

        public static void Attach(
            ISpaBuilder spaBuilder,
            string npmScriptName)
        {
            var sourcePath = spaBuilder.Options.SourcePath;

            if (string.IsNullOrEmpty(sourcePath))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new InvalidOperationException("Must set ISpaBuilder.Options.SourcePath before calling this method.");
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            if (string.IsNullOrEmpty(npmScriptName))
            {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
                throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
#pragma warning restore CA1303 // Do not pass literals as localized parameters
            }

            var appBuilder = spaBuilder.ApplicationBuilder;
            var logger     = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);

            // Clear files in distribution directory aka spaStaticFileOptions.RootPath:
            var spaStaticFileProvider = spaBuilder.ApplicationBuilder.ApplicationServices.GetService(typeof(ISpaStaticFileProvider)) as ISpaStaticFileProvider;
            ClearSpaRootPath(spaStaticFileProvider.FileProvider, logger);

            // Start webpack-dev-server once the application has started
            var        hostApplicationLifetime = spaBuilder.ApplicationBuilder.ApplicationServices.GetService(typeof(IHostApplicationLifetime)) as IHostApplicationLifetime;
            Task <int> portTask         = null;
            Task <Uri> targetUriTask    = null;
            var        socketPortNumber = 0;
            hostApplicationLifetime.ApplicationStarted.Register(() =>
            {
                // When this is called the request pipeline configuration has completed. Only now the addresses
                // at which requests are served are available. We use any address/port combination but use HTTPs
                // if is configured for the project.
                var addressFeature = spaBuilder.ApplicationBuilder.ServerFeatures.Get <IServerAddressesFeature>();
                foreach (var addr in addressFeature.Addresses)
                {
                    var uri          = new Uri(addr);
                    socketPortNumber = uri.Port;
                    if (uri.Scheme == "https")
                    {
                        break;
                    }
                }

                portTask = StartWebpackDevServerAsync(sourcePath, npmScriptName, logger, socketPortNumber);
                // Everything we **proxy** is hardcoded to target http://localhost because:
                // - the requests are always from the local machine (we're not accepting remote
                //   requests that go directly to the webpack-dev-server)
                // - given that, there's no reason to use https when forwarding the request to the webpack
                //   dev server, and we couldn't even if we wanted to, because in general the webpack-dev-server
                //   has no certificate
#pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler
                targetUriTask = portTask.ContinueWith(task =>
                {
                    // "https" here doesn't work as the webpack-dev-server expects request via "http"
                    Uri uri = new UriBuilder("http", "localhost", task.Result).Uri;
                    return(uri);
                });
#pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler
            });

            // Configure proxying. By the time a request comes in, the webpack dev server will be running,
            // so it is fine to configure proxying before the webpack-dev-server has been started.
            SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
            {
                // On each request, we create a separate startup task with its own timeout. That way, even if
                // the first request times out, subsequent requests could still work.
                var timeout = spaBuilder.Options.StartupTimeout;
                return(targetUriTask.WithTimeout(timeout,
                                                 $"The webpack-dev-server did not start listening for requests " +
                                                 $"within the timeout period of {timeout.Seconds} seconds. " +
                                                 $"Check the log output for error information."));
            });
        }