client/Apache.ShenYu.AspNetCore/Services/ShenyuStartupService.cs (171 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Apache.ShenYu.Client.Attributes; using Apache.ShenYu.Client.Models.DTO; using Apache.ShenYu.Client.Options; using Apache.ShenYu.Client.Registers; using Apache.ShenYu.Client.Utils; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Apache.ShenYu.AspNetCore.Services { public class ShenyuStartupService : IHostedService { private readonly ILogger<ShenyuStartupService> _logger; private readonly IServer _server; private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly IShenyuRegister _shenyuRegister; private readonly IOptions<ShenyuOptions> _shenyuOptions; private readonly ApplicationPartManager _partManager; public ShenyuStartupService( ILogger<ShenyuStartupService> logger, IServer server, IHostApplicationLifetime hostApplicationLifetime, IShenyuRegister shenyuRegister, IOptions<ShenyuOptions> shenyuOptions, ApplicationPartManager partManager ) { this._logger = logger; this._server = server; this._hostApplicationLifetime = hostApplicationLifetime; this._shenyuRegister = shenyuRegister; this._shenyuOptions = shenyuOptions; this._partManager = partManager; } public Task StartAsync(CancellationToken cancellationToken) { this._hostApplicationLifetime.ApplicationStarted.Register(async () => { await this._shenyuRegister.Init(this._shenyuOptions.Value); var addresses = this.GetAddresses(); var ipAddresses = GetIpAddresses(addresses); var rpcTypes = this._shenyuOptions.Value.Client.ClientType .Split(',') .Select(type => type.Trim()) .ToList(); foreach (var address in ipAddresses) { var uri = new Uri(address); if (!rpcTypes.Contains(uri.Scheme)) { continue; } // if isFull equals to true, set the whole application as proxy if (this._shenyuOptions.Value.Client.IsFull) { await this._shenyuRegister.PersistInterface(this.BuildMetadataDto(null, address)); } else { var controllerFeature = new ControllerFeature(); this._partManager.PopulateFeature(controllerFeature); var controllers = controllerFeature.Controllers; // traverse all actions foreach (var controller in controllers) { var shenyuClientAttr = controller.GetCustomAttribute<ShenyuClientAttribute>(); if (shenyuClientAttr != null && shenyuClientAttr.Path.Contains("**")) { // proxy the whole controller await this._shenyuRegister.PersistInterface(this.BuildMetadataDto(shenyuClientAttr.Path, address)); continue; } var routeAttr = controller.GetCustomAttribute<RouteAttribute>(); if (routeAttr == null) { continue; } var basePath = this.GetPath(shenyuClientAttr, routeAttr, controller.Name); var methods = controller.GetMethods(); foreach (var method in methods) { // only support action with one HttpMethodAttribute for now var methodRouteAttr = method.GetCustomAttribute<HttpMethodAttribute>(); if (methodRouteAttr == null) { continue; } var methodShenyuAttr = method.GetCustomAttribute<ShenyuClientAttribute>(); var path = this.GetPath(methodShenyuAttr, methodRouteAttr); var wholePath = path == null ? basePath : Path.Combine(Constants.PathSeparator, basePath, path.TrimStart(Convert.ToChar(Constants.PathSeparator))); //fix if wholePath like "path01\\path02" if(wholePath != null && wholePath.Contains(Constants.DoubleSlash)){ wholePath = wholePath.Replace(Constants.DoubleSlash, Constants.PathSeparator); } await this._shenyuRegister.PersistInterface(this.BuildMetadataDto(wholePath, address)); } } } await this._shenyuRegister.PersistURI(this.BuildUriRegisterDto(address)); } }); return Task.CompletedTask; } private ICollection<string> GetIpAddresses(ICollection<string> addresses) { var ipAddresses = new HashSet<string>(); var ip = IpUtils.GetLocalIPv4(NetworkInterfaceType.Ethernet); foreach (var address in addresses) { var uriBuilder = new UriBuilder(address); uriBuilder.Host = ip; var ipAddr = uriBuilder.Uri.ToString(); ipAddresses.Add(ipAddr); } return ipAddresses; } private ICollection<string> GetAddresses() { return this._server.Features.Get<IServerAddressesFeature>().Addresses; } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; private MetaDataRegisterDTO BuildMetadataDto(string path, string address) { var uri = new Uri(address); return new MetaDataRegisterDTO { appName = this._shenyuOptions.Value.Client.AppName, contextPath = this._shenyuOptions.Value.Client.ContextPath, path = this._shenyuOptions.Value.Client.ContextPath + (string.IsNullOrEmpty(path) ? "/**" : path), rpcType = uri.Scheme, ruleName = this._shenyuOptions.Value.Client.ContextPath + (string.IsNullOrEmpty(path) ? "/**" : path), enabled = true, }; } private URIRegisterDTO BuildUriRegisterDto(string address) { var uri = new Uri(address); return new URIRegisterDTO { protocol = $"{uri.Scheme}://", appName = this._shenyuOptions.Value.Client.AppName, contextPath = this._shenyuOptions.Value.Client.ContextPath, rpcType = this._shenyuOptions.Value.Client.ClientType, host = uri.Host, port = uri.Port, }; } private string GetPath(ShenyuClientAttribute shenyuClientAttribute, IRouteTemplateProvider routeAttribute, string controllerName = null) { string routePath = routeAttribute.Template == null ? "" : routeAttribute.Template; if (routePath.Equals("[controller]") && controllerName != null) { routePath = controllerName .Substring(0, controllerName.IndexOf("Controller", StringComparison.Ordinal)) .ToLower(); } return shenyuClientAttribute != null ? shenyuClientAttribute.Path : routePath; } } }