├── .github └── FUNDING.yml ├── .gitignore ├── .vs ├── BeetleX.FastHttpApi │ ├── v15 │ │ └── .suo │ └── v16 │ │ └── TestStore │ │ └── 0 │ │ ├── 000.testlog │ │ └── testlog.manifest └── config │ └── applicationhost.config ├── BeetleX.FastHttpApi.sln ├── BeetleX.FastHttpApi.v11.suo ├── Extend ├── BeetleX.FastHttpApi.Admin │ ├── AdminController.cs │ ├── AdminController.js │ ├── ApiInfo.cs │ ├── ApiInfo.js │ ├── BeetleX.FastHttpApi.Admin.csproj │ ├── BeetleX.FastHttpApi.Admin.csproj.user │ ├── FileManagerController.cs │ ├── FileManagerController.js │ └── views │ │ └── _admin │ │ ├── AdminController.js │ │ ├── Connections.html │ │ ├── FastHttpApi.js │ │ ├── FileManager.html │ │ ├── FileManagerController.js │ │ ├── LogOutput.html │ │ ├── Login.html │ │ ├── ModuleLoader.js │ │ ├── Monitor.js │ │ ├── PublicModule.html │ │ ├── ReadFileHandler.js │ │ ├── api.html │ │ ├── apiscript.html │ │ ├── bootstrap-theme.css │ │ ├── bootstrap.css │ │ ├── bootstrap.js │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ ├── glyphicons-halflings-regular.woff2 │ │ ├── index.html │ │ ├── jquery.js │ │ ├── md5.js │ │ └── vue.js ├── BeetleX.FastHttpApi.Hosting │ ├── BeetleX.FastHttpApi.Hosting.csproj │ └── FastHttpApiHosting.cs └── BeetleX.FastHttpApi.SpanJson │ ├── BeetleX.FastHttpApi.SpanJson.csproj │ ├── BeetleX.FastHttpApi.SpanJson.csproj.user │ └── SpanJsonResult.cs ├── LICENSE ├── PerformanceTest ├── Beetlex_VS_AspCore_webapi │ ├── AspCoreWebapi │ │ ├── AspCoreWebapi.csproj │ │ ├── AspCoreWebapi.csproj.user │ │ ├── Controllers │ │ │ └── HomeController.cs │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ ├── BeetlexWebapi │ │ ├── BeetlexWebapi.csproj │ │ ├── BeetlexWebapi.csproj.user │ │ └── Program.cs │ └── Beetlex_VS_AspCore.sln ├── FastHttpApiWeb │ ├── App.config │ ├── FastHttpApiWeb.csproj │ ├── FastHttpApiWeb.csproj.user │ ├── HttpConfig.json │ └── Program.cs ├── go │ └── main.go ├── netcore-mvc │ ├── .vs │ │ └── netcore-mvc │ │ │ └── v15 │ │ │ └── .suo │ ├── Controllers │ │ └── ValuesController.cs │ ├── Program.cs │ ├── Startup.cs │ ├── netcore-mvc.csproj │ └── netcore-mvc.csproj.user └── netcore │ ├── Program.cs │ ├── Startup.cs │ ├── netcore.csproj │ └── netcore.csproj.user ├── README.md ├── WebApi_json_vs_protobuf.zip ├── _config.yml └── src ├── ActionContext.cs ├── ActionFilterAttribute.cs ├── ActionHandler.cs ├── ActionHandlerFactory.cs ├── ActionResult.cs ├── ActionSettings.cs ├── AuthMarkAttribute.cs ├── AutoLoaderAttribute.cs ├── BeetleX.FastHttpApi.csproj ├── BeetleX.FastHttpApi.csproj.user ├── Beetlex.FastHttpApi.pfx ├── Clients ├── ClientActionHanler.cs ├── HttpApiBase.cs ├── HttpClient.cs ├── HttpClientException.cs ├── HttpClientPacket.cs ├── HttpClusterApiBase.cs ├── IBodyFormater.cs ├── INodeSourcesHandler.cs ├── Request.cs └── Response.cs ├── CommandLineArgs.cs ├── ConfigBase.cs ├── ContenType.cs ├── ControllerAttribute.cs ├── Cookies.cs ├── DatBuffer.cs ├── Data ├── DataContext.cs ├── DataContextBinder.cs ├── DataConvertAttribute.cs ├── IDataContext.cs ├── InvalidDataFilter.cs └── NoDataConvertAttribute.cs ├── EventArgs.cs ├── FastHttpApi.js ├── FileLog.cs ├── GMTDate.cs ├── Header.cs ├── HeaderStringHelper.cs ├── HttpApiServer.cs ├── HttpApiServer_map.cs ├── HttpConfig.json ├── HttpOptions.cs ├── HttpPacket.cs ├── HttpParse.cs ├── HttpRequest.cs ├── HttpResponse.cs ├── HttpToken.cs ├── IActionContextParameter.cs ├── IActionResultHandler.cs ├── IDGenerator.cs ├── IDataContext.cs ├── IDataResponse.cs ├── IHttpContext.cs ├── IPLimit.cs ├── IPv4Tables.cs ├── IResult.cs ├── IRpsLimitHandler.cs ├── LRUCached.cs ├── ModuleManage.cs ├── NextQueue.cs ├── NotLoadResourceAttribute.cs ├── ObjectPool.cs ├── OptionsAttribute.cs ├── PostAttribute.cs ├── PostFile.cs ├── Properties └── PublishProfiles │ ├── FolderProfile.pubxml │ └── FolderProfile.pubxml.user ├── QueryString.cs ├── Route ├── RouteAttribute.cs ├── RouteGroup.cs ├── RouteMatchResult.cs ├── RouteRewrite.cs ├── RouteTemplateMatch.cs └── UrlRoute.cs ├── RpsLimit.cs ├── SameSiteType.cs ├── ServerController.cs ├── ServerCounter.cs ├── ServerStatusController.cs ├── SessionControllerFactory.cs ├── SkipFilterAttribute.cs ├── StaticResurce ├── EventFileResponseArgs.cs ├── EventFindFileArgs.cs ├── FileBlock.cs ├── FileContentType.cs ├── FileResource.cs └── ResourceCenter.cs ├── Statistics.cs ├── Utils.cs ├── Validations ├── IValidationOutputHandler.cs ├── Regex.cs ├── ValidationBase.cs └── ValueRegion.cs ├── VirtualFolder.cs └── WebSockets ├── DataFrame.cs ├── DataPackeType.cs ├── EventWebSocketReceive.cs ├── IDataPacketSerializer.cs ├── IWebSocketServer.cs ├── WebSocketPacket.cs ├── WebSocketServer.cs └── WebSocketToken.cs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: beetlex 2 | custom: ["https://www.paypal.me/henryfancn","http://beetlex.io"] 3 | -------------------------------------------------------------------------------- /.vs/BeetleX.FastHttpApi/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/.vs/BeetleX.FastHttpApi/v15/.suo -------------------------------------------------------------------------------- /.vs/BeetleX.FastHttpApi/v16/TestStore/0/000.testlog: -------------------------------------------------------------------------------- 1 | !!tItseT -------------------------------------------------------------------------------- /.vs/BeetleX.FastHttpApi/v16/TestStore/0/testlog.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/.vs/BeetleX.FastHttpApi/v16/TestStore/0/testlog.manifest -------------------------------------------------------------------------------- /BeetleX.FastHttpApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeetleX.FastHttpApi", "src\BeetleX.FastHttpApi.csproj", "{F3F9B720-851A-4BDE-A2EC-EF98B7C8B6A9}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeetleX.FastHttpApi.Admin", "Extend\BeetleX.FastHttpApi.Admin\BeetleX.FastHttpApi.Admin.csproj", "{343769E9-1292-456D-B55A-A0F0C5A9E6A6}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PerformanceTest", "PerformanceTest", "{D3C2884A-0DFC-45ED-ABD5-492564E20F0B}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastHttpApiWeb", "PerformanceTest\FastHttpApiWeb\FastHttpApiWeb.csproj", "{25013304-8D45-4034-9126-20D4BE0EC306}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "netcore", "PerformanceTest\netcore\netcore.csproj", "{72510BA9-384B-4F00-BA90-676F7A75180C}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "netcore-mvc", "PerformanceTest\netcore-mvc\netcore-mvc.csproj", "{CE2A182B-F9E6-4EBC-8974-17065E550856}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "go", "go", "{91CE5B83-AA5B-44D0-86FF-A655F55EB77C}" 19 | ProjectSection(SolutionItems) = preProject 20 | PerformanceTest\go\main.go = PerformanceTest\go\main.go 21 | EndProjectSection 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeetleX.FastHttpApi.SpanJson", "Extend\BeetleX.FastHttpApi.SpanJson\BeetleX.FastHttpApi.SpanJson.csproj", "{FC3E4F02-CF05-44F0-93A2-5FDBDB0F3702}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {F3F9B720-851A-4BDE-A2EC-EF98B7C8B6A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {F3F9B720-851A-4BDE-A2EC-EF98B7C8B6A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {F3F9B720-851A-4BDE-A2EC-EF98B7C8B6A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {F3F9B720-851A-4BDE-A2EC-EF98B7C8B6A9}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {343769E9-1292-456D-B55A-A0F0C5A9E6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {343769E9-1292-456D-B55A-A0F0C5A9E6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {343769E9-1292-456D-B55A-A0F0C5A9E6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {343769E9-1292-456D-B55A-A0F0C5A9E6A6}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {25013304-8D45-4034-9126-20D4BE0EC306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {25013304-8D45-4034-9126-20D4BE0EC306}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {25013304-8D45-4034-9126-20D4BE0EC306}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {25013304-8D45-4034-9126-20D4BE0EC306}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {72510BA9-384B-4F00-BA90-676F7A75180C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {72510BA9-384B-4F00-BA90-676F7A75180C}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {72510BA9-384B-4F00-BA90-676F7A75180C}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {72510BA9-384B-4F00-BA90-676F7A75180C}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {CE2A182B-F9E6-4EBC-8974-17065E550856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {CE2A182B-F9E6-4EBC-8974-17065E550856}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {CE2A182B-F9E6-4EBC-8974-17065E550856}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {CE2A182B-F9E6-4EBC-8974-17065E550856}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {FC3E4F02-CF05-44F0-93A2-5FDBDB0F3702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {FC3E4F02-CF05-44F0-93A2-5FDBDB0F3702}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {FC3E4F02-CF05-44F0-93A2-5FDBDB0F3702}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {FC3E4F02-CF05-44F0-93A2-5FDBDB0F3702}.Release|Any CPU.Build.0 = Release|Any CPU 55 | EndGlobalSection 56 | GlobalSection(SolutionProperties) = preSolution 57 | HideSolutionNode = FALSE 58 | EndGlobalSection 59 | GlobalSection(NestedProjects) = preSolution 60 | {25013304-8D45-4034-9126-20D4BE0EC306} = {D3C2884A-0DFC-45ED-ABD5-492564E20F0B} 61 | {72510BA9-384B-4F00-BA90-676F7A75180C} = {D3C2884A-0DFC-45ED-ABD5-492564E20F0B} 62 | {CE2A182B-F9E6-4EBC-8974-17065E550856} = {D3C2884A-0DFC-45ED-ABD5-492564E20F0B} 63 | {91CE5B83-AA5B-44D0-86FF-A655F55EB77C} = {D3C2884A-0DFC-45ED-ABD5-492564E20F0B} 64 | EndGlobalSection 65 | GlobalSection(ExtensibilityGlobals) = postSolution 66 | SolutionGuid = {988EEA11-1C48-44A4-9DF0-1C3D08600A4F} 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /BeetleX.FastHttpApi.v11.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/BeetleX.FastHttpApi.v11.suo -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/AdminController.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | FastHttpApi javascript api Generator Copyright © henryfan 2018 email:henryfan@msn.com 3 | https://github.com/IKende/FastHttpApi 4 | **************************************************************************************/ 5 | 6 | 7 | 8 | 9 | var _AdminListApiUrl='/_admin/ListApi'; 10 | /** 11 | * '_AdminListApi(params).execute(function(result){});' 12 | **/ 13 | function _AdminListApi(useHttp) 14 | { 15 | return api(_AdminListApiUrl,{},useHttp); 16 | } 17 | var _AdminGetKeyUrl='/_admin/GetKey'; 18 | /** 19 | * '_AdminGetKey(params).execute(function(result){});' 20 | **/ 21 | function _AdminGetKey(useHttp) 22 | { 23 | return api(_AdminGetKeyUrl,{},useHttp); 24 | } 25 | var _AdminGetSettingInfoUrl='/_admin/GetSettingInfo'; 26 | /** 27 | * '_AdminGetSettingInfo(params).execute(function(result){});' 28 | **/ 29 | function _AdminGetSettingInfo(useHttp) 30 | { 31 | return api(_AdminGetSettingInfoUrl,{},useHttp); 32 | } 33 | var _AdminSettingUrl='/_admin/Setting'; 34 | /** 35 | * '_AdminSetting(params).execute(function(result){});' 36 | **/ 37 | function _AdminSetting(setting,useHttp) 38 | { 39 | return api(_AdminSettingUrl,{setting:setting},useHttp,true); 40 | } 41 | var _AdminLogConnectUrl='/_admin/LogConnect'; 42 | /** 43 | * '_AdminLogConnect(params).execute(function(result){});' 44 | **/ 45 | function _AdminLogConnect(useHttp) 46 | { 47 | return api(_AdminLogConnectUrl,{},useHttp); 48 | } 49 | var _AdminLogDisConnectUrl='/_admin/LogDisConnect'; 50 | /** 51 | * '_AdminLogDisConnect(params).execute(function(result){});' 52 | **/ 53 | function _AdminLogDisConnect(useHttp) 54 | { 55 | return api(_AdminLogDisConnectUrl,{},useHttp); 56 | } 57 | var _AdminCloseSessionUrl='/_admin/CloseSession'; 58 | /** 59 | * '_AdminCloseSession(params).execute(function(result){});' 60 | **/ 61 | function _AdminCloseSession(items,useHttp) 62 | { 63 | return api(_AdminCloseSessionUrl,{items:items},useHttp,true); 64 | } 65 | var _AdminGetServerInfoUrl='/_admin/GetServerInfo'; 66 | /** 67 | * '_AdminGetServerInfo(params).execute(function(result){});' 68 | **/ 69 | function _AdminGetServerInfo(useHttp) 70 | { 71 | return api(_AdminGetServerInfoUrl,{},useHttp); 72 | } 73 | var _AdminListConnectionUrl='/_admin/ListConnection'; 74 | /** 75 | * '_AdminListConnection(params).execute(function(result){});' 76 | **/ 77 | function _AdminListConnection(index,useHttp) 78 | { 79 | return api(_AdminListConnectionUrl,{index:index},useHttp); 80 | } 81 | var _AdminLoginUrl='/_admin/Login'; 82 | /** 83 | * '_AdminLogin(params).execute(function(result){});' 84 | **/ 85 | function _AdminLogin(name,pwd,useHttp) 86 | { 87 | return api(_AdminLoginUrl,{name:name,pwd:pwd},useHttp); 88 | } 89 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/ApiInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace BeetleX.FastHttpApi.Admin 7 | { 8 | class UrlInfo : IComparable 9 | { 10 | public UrlInfo(ActionHandler handler) 11 | { 12 | Url = handler.SourceUrl; 13 | Http = new HttpInvoke(); 14 | Http.Build(handler); 15 | WebSocket = new WebSocketInvoke(); 16 | WebSocket.Build(handler); 17 | Remark = handler.Remark; 18 | Handler = handler; 19 | } 20 | 21 | internal ActionHandler Handler { get; set; } 22 | 23 | public string Remark { get; set; } 24 | 25 | public string Url { get; set; } 26 | 27 | public HttpInvoke Http { get; set; } 28 | 29 | public WebSocketInvoke WebSocket { get; set; } 30 | 31 | public int Compare(UrlInfo x, UrlInfo y) 32 | { 33 | return x.Url.CompareTo(y.Url); 34 | } 35 | 36 | public int CompareTo(object obj) 37 | { 38 | return this.Url.CompareTo(((UrlInfo)obj).Url); 39 | } 40 | } 41 | 42 | abstract class InvokeInfo 43 | { 44 | public string Url { get; set; } 45 | 46 | public string Body { get; set; } 47 | 48 | public string Response { get; set; } 49 | 50 | public abstract void Build(ActionHandler handler); 51 | 52 | 53 | } 54 | 55 | class HttpInvoke : InvokeInfo 56 | { 57 | public override void Build(ActionHandler handler) 58 | { 59 | Url = handler.SourceUrl; 60 | int k = 0; 61 | foreach (ParameterBinder item in handler.Parameters) 62 | { 63 | if (!item.DataParameter) 64 | continue; 65 | if (k == 0) 66 | { 67 | Url += "?"; 68 | } 69 | else 70 | { 71 | Url += "&"; 72 | } 73 | Url += item.Name + "=" + item.DefaultValue().ToString(); 74 | k++; 75 | 76 | } 77 | Response = Newtonsoft.Json.JsonConvert.SerializeObject(new ActionResult()); 78 | } 79 | } 80 | 81 | class WebSocketInvoke : InvokeInfo 82 | { 83 | public override void Build(ActionHandler handler) 84 | { 85 | Url = handler.SourceUrl; 86 | Dictionary mParams = new Dictionary(); 87 | foreach (ParameterBinder item in handler.Parameters) 88 | { 89 | if (item.DataParameter) 90 | { 91 | 92 | mParams[item.Name] = item.DefaultValue(); 93 | 94 | } 95 | } 96 | Dictionary msg = new Dictionary(); 97 | msg["url"] = handler.SourceUrl; 98 | msg["params"] = mParams; 99 | try 100 | { 101 | Body = Newtonsoft.Json.JsonConvert.SerializeObject(msg); 102 | } 103 | catch 104 | { 105 | Body = "{}"; 106 | } 107 | Response = Newtonsoft.Json.JsonConvert.SerializeObject(new ActionResult()); 108 | } 109 | } 110 | 111 | public class ParameterInfo 112 | { 113 | public string Name { get; set; } 114 | 115 | public object Value { get; set; } 116 | 117 | public bool IsBody { get; set; } 118 | 119 | public Type Type { get; set; } 120 | 121 | public override string ToString() 122 | { 123 | string value = Newtonsoft.Json.JsonConvert.SerializeObject(Value); 124 | if (Value is IEnumerable && Value.GetType().IsGenericType) 125 | { 126 | try 127 | { 128 | System.Collections.ArrayList list = new ArrayList(); 129 | Type[] subtype = Value.GetType().GetGenericArguments(); 130 | list.Add(Activator.CreateInstance(subtype[0])); 131 | list.Add(Activator.CreateInstance(subtype[0])); 132 | value = Newtonsoft.Json.JsonConvert.SerializeObject(list); 133 | } 134 | catch 135 | { } 136 | } 137 | return value.ToString(); 138 | } 139 | 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/ApiInfo.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | FastHttpApi javascript api Generator Copyright © henryfan 2018 email:henryfan@msn.com 3 | https://github.com/IKende/FastHttpApi 4 | **************************************************************************************/ 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/BeetleX.FastHttpApi.Admin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 0.6.2 6 | henryfan 7 | ikende.com 8 | dotnet core fast http api server manager 9 | 10 | Copyright © ikende.com 2018 email:henryfan@msn.com 11 | https://github.com/IKende/FastHttpApi/blob/master/LICENSE.md/LICENSE.md 12 | https://github.com/IKende/FastHttpApi 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Never 52 | 53 | 54 | 55 | Never 56 | 57 | 58 | Never 59 | 60 | 61 | Never 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | Never 70 | 71 | 72 | 73 | Never 74 | 75 | 76 | Never 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | AdminController.js 88 | JSAPI 89 | 90 | 91 | JSAPI 92 | ApiInfo.js 93 | 94 | 95 | FileManagerController.js 96 | JSAPI 97 | 98 | 99 | 100 | 101 | 102 | AdminController.cs 103 | True 104 | True 105 | 106 | 107 | True 108 | True 109 | ApiInfo.cs 110 | 111 | 112 | FileManagerController.cs 113 | True 114 | True 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/BeetleX.FastHttpApi.Admin.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | <_LastSelectedProfileId>D:\VisualStudio\BitHub\FastHttpApi\Extend\BeetleX.FastHttpApi.Admin\Properties\PublishProfiles\FolderProfile.pubxml 5 | 6 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/FileManagerController.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | FastHttpApi javascript api Generator Copyright © henryfan 2018 email:henryfan@msn.com 3 | https://github.com/IKende/FastHttpApi 4 | **************************************************************************************/ 5 | 6 | 7 | 8 | 9 | var _FileManagerCreateFolderUrl='/_admin/files/CreateFolder'; 10 | /** 11 | * '_FileManagerCreateFolder(params).execute(function(result){});' 12 | **/ 13 | function _FileManagerCreateFolder(folder,name,useHttp) 14 | { 15 | return api(_FileManagerCreateFolderUrl,{folder:folder,name:name},useHttp); 16 | } 17 | var _FileManagerUploadFileUrl='/_admin/files/UploadFile'; 18 | /** 19 | * '_FileManagerUploadFile(params).execute(function(result){});' 20 | **/ 21 | function _FileManagerUploadFile(folder,info,useHttp) 22 | { 23 | return api(_FileManagerUploadFileUrl,{folder:folder,info:info},useHttp,true); 24 | } 25 | var _FileManagerDeleteResourceUrl='/_admin/files/DeleteResource'; 26 | /** 27 | * '_FileManagerDeleteResource(params).execute(function(result){});' 28 | **/ 29 | function _FileManagerDeleteResource(folder,name,file,useHttp) 30 | { 31 | return api(_FileManagerDeleteResourceUrl,{folder:folder,name:name,file:file},useHttp); 32 | } 33 | var _FileManagerListUrl='/_admin/files/List'; 34 | /** 35 | * '_FileManagerList(params).execute(function(result){});' 36 | **/ 37 | function _FileManagerList(folder,useHttp) 38 | { 39 | return api(_FileManagerListUrl,{folder:folder},useHttp); 40 | } 41 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/AdminController.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | FastHttpApi javascript api Generator Copyright © henryfan 2018 email:henryfan@msn.com 3 | https://github.com/IKende/FastHttpApi 4 | **************************************************************************************/ 5 | 6 | 7 | 8 | 9 | var _AdminListApiUrl = '/_admin/listapi'; 10 | /** 11 | * 'var result= await _AdminListApi(params);' 12 | **/ 13 | function _AdminListApi(useHttp) { 14 | return api(_AdminListApiUrl, {}, useHttp).sync(); 15 | } 16 | /** 17 | * '_AdminListApiAsync(params).execute(function(result){},useHttp);' 18 | **/ 19 | function _AdminListApiAsync(useHttp) { 20 | return api(_AdminListApiUrl, {}, useHttp); 21 | } 22 | var _AdminGetKeyUrl = '/_admin/getkey'; 23 | /** 24 | * 'var result= await _AdminGetKey(params);' 25 | **/ 26 | function _AdminGetKey(useHttp) { 27 | return api(_AdminGetKeyUrl, {}, useHttp).sync(); 28 | } 29 | /** 30 | * '_AdminGetKeyAsync(params).execute(function(result){},useHttp);' 31 | **/ 32 | function _AdminGetKeyAsync(useHttp) { 33 | return api(_AdminGetKeyUrl, {}, useHttp); 34 | } 35 | var _AdminGetSettingInfoUrl = '/_admin/getsettinginfo'; 36 | /** 37 | * 'var result= await _AdminGetSettingInfo(params);' 38 | **/ 39 | function _AdminGetSettingInfo(useHttp) { 40 | return api(_AdminGetSettingInfoUrl, {}, useHttp).sync(); 41 | } 42 | /** 43 | * '_AdminGetSettingInfoAsync(params).execute(function(result){},useHttp);' 44 | **/ 45 | function _AdminGetSettingInfoAsync(useHttp) { 46 | return api(_AdminGetSettingInfoUrl, {}, useHttp); 47 | } 48 | var _AdminSettingUrl = '/_admin/setting'; 49 | /** 50 | * 'var result= await _AdminSetting(params);' 51 | **/ 52 | function _AdminSetting(setting, useHttp) { 53 | return api(_AdminSettingUrl, { setting: setting }, useHttp, true).sync(); 54 | } 55 | /** 56 | * '_AdminSettingAsync(params).execute(function(result){},useHttp);' 57 | **/ 58 | function _AdminSettingAsync(setting, useHttp) { 59 | return api(_AdminSettingUrl, { setting: setting }, useHttp, true); 60 | } 61 | var _AdminLogConnectUrl = '/_admin/logconnect'; 62 | /** 63 | * 'var result= await _AdminLogConnect(params);' 64 | **/ 65 | function _AdminLogConnect(useHttp) { 66 | return api(_AdminLogConnectUrl, {}, useHttp).sync(); 67 | } 68 | /** 69 | * '_AdminLogConnectAsync(params).execute(function(result){},useHttp);' 70 | **/ 71 | function _AdminLogConnectAsync(useHttp) { 72 | return api(_AdminLogConnectUrl, {}, useHttp); 73 | } 74 | var _AdminLogDisConnectUrl = '/_admin/logdisconnect'; 75 | /** 76 | * 'var result= await _AdminLogDisConnect(params);' 77 | **/ 78 | function _AdminLogDisConnect(useHttp) { 79 | return api(_AdminLogDisConnectUrl, {}, useHttp).sync(); 80 | } 81 | /** 82 | * '_AdminLogDisConnectAsync(params).execute(function(result){},useHttp);' 83 | **/ 84 | function _AdminLogDisConnectAsync(useHttp) { 85 | return api(_AdminLogDisConnectUrl, {}, useHttp); 86 | } 87 | var _AdminCloseSessionUrl = '/_admin/closesession'; 88 | /** 89 | * 'var result= await _AdminCloseSession(params);' 90 | **/ 91 | function _AdminCloseSession(items, useHttp) { 92 | return api(_AdminCloseSessionUrl, { items: items }, useHttp, true).sync(); 93 | } 94 | /** 95 | * '_AdminCloseSessionAsync(params).execute(function(result){},useHttp);' 96 | **/ 97 | function _AdminCloseSessionAsync(items, useHttp) { 98 | return api(_AdminCloseSessionUrl, { items: items }, useHttp, true); 99 | } 100 | var _AdminGetServerInfoUrl = '/_admin/getserverinfo'; 101 | /** 102 | * 'var result= await _AdminGetServerInfo(params);' 103 | **/ 104 | function _AdminGetServerInfo(useHttp) { 105 | return api(_AdminGetServerInfoUrl, {}, useHttp).sync(); 106 | } 107 | /** 108 | * '_AdminGetServerInfoAsync(params).execute(function(result){},useHttp);' 109 | **/ 110 | function _AdminGetServerInfoAsync(useHttp) { 111 | return api(_AdminGetServerInfoUrl, {}, useHttp); 112 | } 113 | var _AdminListConnectionUrl = '/_admin/listconnection'; 114 | /** 115 | * 'var result= await _AdminListConnection(params);' 116 | **/ 117 | function _AdminListConnection(index, useHttp) { 118 | return api(_AdminListConnectionUrl, { index: index }, useHttp).sync(); 119 | } 120 | /** 121 | * '_AdminListConnectionAsync(params).execute(function(result){},useHttp);' 122 | **/ 123 | function _AdminListConnectionAsync(index, useHttp) { 124 | return api(_AdminListConnectionUrl, { index: index }, useHttp); 125 | } 126 | var _AdminLoginUrl = '/_admin/login'; 127 | /** 128 | * 'var result= await _AdminLogin(params);' 129 | **/ 130 | function _AdminLogin(name, pwd, useHttp) { 131 | return api(_AdminLoginUrl, { name: name, pwd: pwd }, useHttp).sync(); 132 | } 133 | /** 134 | * '_AdminLoginAsync(params).execute(function(result){},useHttp);' 135 | **/ 136 | function _AdminLoginAsync(name, pwd, useHttp) { 137 | return api(_AdminLoginUrl, { name: name, pwd: pwd }, useHttp); 138 | } 139 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/FastHttpApi.js: -------------------------------------------------------------------------------- 1 | var __id = 0; 2 | var __receive; 3 | var __connect; 4 | var __disconnect; 5 | function FastHttpApiWebSocket() { 6 | this.wsUri = "ws://" + window.location.host; 7 | this.websocket; 8 | this.status = false; 9 | this.messagHandlers = new Object(); 10 | } 11 | 12 | FastHttpApiWebSocket.prototype.send = function (url, params, callback) { 13 | if (this.status == false) { 14 | if (callback != null) { 15 | callback({ Url: url, Code: 505, Error: 'disconnect' }) 16 | } 17 | } 18 | this.messagHandlers[params._requestid] = callback; 19 | var data = { url: url, params: params }; 20 | this.websocket.send(JSON.stringify(data)); 21 | } 22 | 23 | FastHttpApiWebSocket.prototype.onOpen = function (evt) { 24 | this.status = true; 25 | if (__connect) 26 | __connect(this); 27 | } 28 | 29 | FastHttpApiWebSocket.prototype.onClose = function (evt) { 30 | this.status = false; 31 | var _this = this; 32 | if (__disconnect) 33 | __disconnect(this); 34 | if (evt.code == 1006) { 35 | setTimeout(function () { 36 | _this.Connect(); 37 | }, 2000); 38 | } 39 | 40 | } 41 | 42 | FastHttpApiWebSocket.prototype.onMessage = function (evt) { 43 | var msg = JSON.parse(evt.data); 44 | var callback = this.messagHandlers[msg.ID]; 45 | if (callback) 46 | callback(msg); 47 | else 48 | if (__receive) 49 | __receive(msg); 50 | } 51 | FastHttpApiWebSocket.prototype.onError = function (evt) { 52 | 53 | } 54 | 55 | FastHttpApiWebSocket.prototype.Connect = function () { 56 | this.websocket = new WebSocket(this.wsUri); 57 | _this = this; 58 | this.websocket.onopen = function (evt) { _this.onOpen(evt) }; 59 | this.websocket.onclose = function (evt) { _this.onClose(evt) }; 60 | this.websocket.onmessage = function (evt) { _this.onMessage(evt) }; 61 | this.websocket.onerror = function (evt) { _this.onError(evt) }; 62 | } 63 | 64 | 65 | function FastHttpApi(url, params, http, post) { 66 | if (http == true) 67 | this.http = true; 68 | else 69 | this.http = false; 70 | this.url = url; 71 | this.post = false; 72 | if (post == true) 73 | this.post = true; 74 | this.params = params; 75 | if (!this.params) 76 | this.params = new Object(); 77 | 78 | } 79 | 80 | FastHttpApi.prototype.sync = function () { 81 | var _this = this; 82 | return new Promise(resolve => { 83 | _this.execute(function (result) { 84 | resolve(result); 85 | }); 86 | }); 87 | } 88 | FastHttpApi.prototype.httpRequest = function () { 89 | this.http = true; 90 | return this.sync(); 91 | } 92 | 93 | FastHttpApi.prototype.execute = function (callback, http) { 94 | if (http == true) 95 | this.http = true; 96 | var id = ++__id; 97 | if (__id > 1024) 98 | __id = 0; 99 | var httpurl; 100 | var keys; 101 | var index; 102 | this.params['_requestid'] = id; 103 | if (this.http || __websocket.status == false) { 104 | if (this.post) { 105 | httpurl = this.url; 106 | $.post(httpurl, JSON.stringify(this.params), function (result) { 107 | if (callback) 108 | callback(result); 109 | }); 110 | } 111 | else { 112 | //get 113 | httpurl = this.url; 114 | keys = Object.keys(this.params); 115 | index = 0; 116 | for (i = 0; i < keys.length; i++) { 117 | if (this.params[keys[i]]) { 118 | if (index == 0) { 119 | httpurl += "?"; 120 | } 121 | else { 122 | httpurl += "&"; 123 | } 124 | httpurl += keys[i] + '=' + encodeURIComponent(this.params[keys[i]]); 125 | index++; 126 | } 127 | } 128 | $.get(httpurl, function (result) { 129 | if (callback) 130 | callback(result); 131 | }); 132 | } 133 | } 134 | else { 135 | __websocket.send(this.url, this.params, callback); 136 | } 137 | 138 | } 139 | 140 | 141 | function api_connect(callback) { 142 | __connect = callback; 143 | } 144 | 145 | function api_disconnect(callback) { 146 | __disconnect = callback; 147 | } 148 | 149 | function api(url, params, http, post) { 150 | return new FastHttpApi(url, params, http, post); 151 | } 152 | 153 | function api_receive(callback) { 154 | __receive = callback; 155 | } 156 | 157 | var __websocket = new FastHttpApiWebSocket(); 158 | __websocket.Connect(); 159 | 160 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/FileManagerController.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | FastHttpApi javascript api Generator Copyright © henryfan 2018 email:henryfan@msn.com 3 | https://github.com/IKende/FastHttpApi 4 | **************************************************************************************/ 5 | 6 | 7 | 8 | 9 | var _FileManagerCreateFolderUrl = '/_admin/files/createfolder'; 10 | /** 11 | * 'var result= await _FileManagerCreateFolder(params);' 12 | **/ 13 | function _FileManagerCreateFolder(folder, name, useHttp) { 14 | return api(_FileManagerCreateFolderUrl, { folder: folder, name: name }, useHttp).sync(); 15 | } 16 | /** 17 | * '_FileManagerCreateFolderAsync(params).execute(function(result){},useHttp);' 18 | **/ 19 | function _FileManagerCreateFolderAsync(folder, name, useHttp) { 20 | return api(_FileManagerCreateFolderUrl, { folder: folder, name: name }, useHttp); 21 | } 22 | var _FileManagerUploadFileUrl = '/_admin/files/uploadfile'; 23 | /** 24 | * 'var result= await _FileManagerUploadFile(params);' 25 | **/ 26 | function _FileManagerUploadFile(folder, info, useHttp) { 27 | return api(_FileManagerUploadFileUrl, { folder: folder, info: info }, useHttp, true).sync(); 28 | } 29 | /** 30 | * '_FileManagerUploadFileAsync(params).execute(function(result){},useHttp);' 31 | **/ 32 | function _FileManagerUploadFileAsync(folder, info, useHttp) { 33 | return api(_FileManagerUploadFileUrl, { folder: folder, info: info }, useHttp, true); 34 | } 35 | var _FileManagerDeleteResourceUrl = '/_admin/files/deleteresource'; 36 | /** 37 | * 'var result= await _FileManagerDeleteResource(params);' 38 | **/ 39 | function _FileManagerDeleteResource(folder, name, file, useHttp) { 40 | return api(_FileManagerDeleteResourceUrl, { folder: folder, name: name, file: file }, useHttp).sync(); 41 | } 42 | /** 43 | * '_FileManagerDeleteResourceAsync(params).execute(function(result){},useHttp);' 44 | **/ 45 | function _FileManagerDeleteResourceAsync(folder, name, file, useHttp) { 46 | return api(_FileManagerDeleteResourceUrl, { folder: folder, name: name, file: file }, useHttp); 47 | } 48 | var _FileManagerListUrl = '/_admin/files/list'; 49 | /** 50 | * 'var result= await _FileManagerList(params);' 51 | **/ 52 | function _FileManagerList(folder, useHttp) { 53 | return api(_FileManagerListUrl, { folder: folder }, useHttp).sync(); 54 | } 55 | /** 56 | * '_FileManagerListAsync(params).execute(function(result){},useHttp);' 57 | **/ 58 | function _FileManagerListAsync(folder, useHttp) { 59 | return api(_FileManagerListUrl, { folder: folder }, useHttp); 60 | } 61 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/LogOutput.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | FastHttpApi Service Management 15 | 45 | 46 | 47 | 52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 |
Log info
60 |
61 |
62 | 63 | 64 | 65 | 66 |
67 |
68 | 69 |
70 |
71 | 72 |
73 | 74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 |
82 | 86 | 87 | 107 | 108 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/Login.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | FastHttpApi Service Management 15 | 16 | 17 | 22 |
23 |
24 | 25 |
26 |
sign
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 | 36 |
37 | 38 |
39 |
40 |
41 |
42 |
43 | 65 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/ModuleLoader.js: -------------------------------------------------------------------------------- 1 | function moduleLoad(url) { 2 | $.get(url, function (result) { 3 | var html = $(result); 4 | var __templates = html; 5 | $("[slot]").each(function () { 6 | var id = $(this).attr('slot'); 7 | var body = $(__templates).find('#' + id).html(); 8 | $(this).html(body); 9 | }); 10 | }); 11 | } 12 | $(document).ready(function () { 13 | moduleLoad("/_admin/PublicModule.html"); 14 | }); -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/Monitor.js: -------------------------------------------------------------------------------- 1 | function MonitorPoint() { 2 | this.x = 0; 3 | this.y = 0; 4 | this.value = 0; 5 | } 6 | function MonitorItem(height, width) { 7 | this.maxValue = 100; 8 | this.changeMaxValue = false; 9 | this.label = ""; 10 | this.items = new Array(); 11 | this.strokeStyle = "chartreuse"; 12 | this.height = height; 13 | this.width = width; 14 | this.lastValue; 15 | wlen = width / 200; 16 | for (i = 0; i <= 200; i++) { 17 | var p = new MonitorPoint(); 18 | p.x = i * wlen; 19 | p.y = this.height; 20 | this.items.push(p); 21 | } 22 | 23 | } 24 | MonitorItem.prototype.push = function (value) { 25 | var length = this.items.length - 1; 26 | for (i = 0; i < length; i++) { 27 | var l = this.items[i]; 28 | var c = this.items[i + 1]; 29 | if (c && l) 30 | l.y = c.y; 31 | } 32 | this.lastValue = value; 33 | var p = this.items[this.items.length - 1]; 34 | if (value >= this.maxValue) { 35 | p.value = this.maxValue - 4; 36 | } 37 | else { 38 | p.value = value; 39 | } 40 | var pe = this.height / this.maxValue; 41 | p.y = this.height - (p.value * pe); 42 | 43 | } 44 | 45 | MonitorItem.prototype.draw = function (context) { 46 | context.beginPath(); 47 | var first = this.items[0]; 48 | context.moveTo(first.x, first.y); 49 | for (i = 0; i < this.items.length; i++) { 50 | var p = this.items[i]; 51 | context.lineTo(p.x, p.y); 52 | } 53 | context.fillStyle = '' 54 | if (this.lastValue >= this.maxValue) 55 | context.strokeStyle = "orangered"; 56 | else 57 | context.strokeStyle = this.strokeStyle; 58 | context.stroke(); 59 | } 60 | 61 | function Monitor(canvas) { 62 | this.colors = ["chartreuse", "aqua", "blueviolet", "darkorange", "deepskyblue", "gold"]; 63 | this.canvas = document.getElementById(canvas); 64 | this.context = this.canvas.getContext('2d'); 65 | this.items = new Array(); 66 | } 67 | Monitor.prototype.draw = function () { 68 | this.clear(); 69 | var _this = this; 70 | this.items.forEach(function (v, i) { 71 | v.draw(_this.context); 72 | }); 73 | } 74 | Monitor.prototype.create = function () { 75 | var item = new MonitorItem(this.canvas.height, this.canvas.width); 76 | item.strokeStyle = this.colors.shift(); 77 | this.items.push(item); 78 | return item; 79 | } 80 | Monitor.prototype.clear = function () { 81 | this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); 82 | this.context.fillStyle = "#000000"; 83 | this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); 84 | var y = 0; 85 | while (y < this.canvas.height) { 86 | this.context.beginPath(); 87 | this.context.moveTo(0, y); 88 | this.context.lineTo(this.canvas.width, y); 89 | this.context.lineWidth = 1; 90 | 91 | // set line color 92 | this.context.strokeStyle = 'darkgreen'; 93 | this.context.stroke(); 94 | y += 12; 95 | } 96 | var x = 0; 97 | while (x < this.canvas.width) { 98 | this.context.beginPath(); 99 | this.context.moveTo(x, 0); 100 | this.context.lineTo(x, this.canvas.height); 101 | this.context.lineWidth = 1; 102 | // set line color 103 | this.context.strokeStyle = 'darkgreen'; 104 | this.context.stroke(); 105 | x += 12; 106 | } 107 | var _ths = this; 108 | this.items.forEach(function (v, i) { 109 | 110 | _ths.context.font = '12px Sans-serif'; 111 | if (v.lastValue > v.maxValue) 112 | _ths.context.fillStyle = 'orangered'; 113 | else 114 | _ths.context.fillStyle = v.strokeStyle 115 | _ths.context.fillText(v.label + " " + v.lastValue + '/' + v.maxValue, 10, i * 14 + 14); 116 | }); 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/PublicModule.html: -------------------------------------------------------------------------------- 1 |  2 | 18 | 27 | 31 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/ReadFileHandler.js: -------------------------------------------------------------------------------- 1 | var _uploadID = 0; 2 | function readFileHandler(file, blockSize) { 3 | this.file = file; 4 | this.size = file.size; 5 | this.readBytes = 0; 6 | this.id = ++_uploadID; 7 | this.index = 0; 8 | this.name = file.name; 9 | this.blockSize = 1024 * 8; 10 | if (blockSize) 11 | this.blockSize = blockSize; 12 | this.pages = parseInt(file.size / this.blockSize); 13 | if (file.size % this.blockSize > 0) 14 | this.pages++; 15 | this.reader = null; 16 | this.percent = 0; 17 | } 18 | 19 | readFileHandler.prototype.completed = function () { 20 | return this.pages == this.index; 21 | }; 22 | readFileHandler.prototype.read = function () { 23 | var _this = this; 24 | 25 | return new Promise(resolve => { 26 | var length = _this.size - this.readBytes; 27 | if (length > _this.blockSize) 28 | length = _this.blockSize; 29 | if (!_this.reader) 30 | _this.reader = new FileReader(); 31 | var result; 32 | _this.reader.onload = function (evt) { 33 | if (evt.target.readyState == FileReader.DONE) { 34 | var str = _this.toBase64(evt.target.result); 35 | result = { Eof: _this.completed(), Data: str, Name: _this.name }; 36 | resolve(result); 37 | } 38 | else { 39 | result = { errCode: 500, name: "load file error!" }; 40 | resolve(result); 41 | } 42 | }; 43 | _this.reader.onerror = function (evt) { 44 | result = { errCode: evt.target.error.errCode, name: evt.target.error.name }; 45 | resolve(result); 46 | }; 47 | 48 | var start = _this.index * _this.blockSize; 49 | var end = start + length; 50 | _this.index++; 51 | _this.readBytes += length; 52 | var blob = _this.file.slice(start, end); 53 | _this.reader.readAsArrayBuffer(blob); 54 | var p = this.index / this.pages * 100; 55 | this.percent = parseInt(p); 56 | }); 57 | }; 58 | readFileHandler.prototype.toBase64 = function (buffer) { 59 | var binary = ''; 60 | var bytes = new Uint8Array(buffer); 61 | var len = bytes.byteLength; 62 | for (var i = 0; i < len; i++) { 63 | binary += String.fromCharCode(bytes[i]); 64 | } 65 | return window.btoa(binary); 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/api.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | FastHttpApi Service Management 16 | 42 | 43 | 44 | 49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | 66 | 135 | 136 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/apiscript.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | FastHttpApi api info 11 | 37 | 38 | 39 | 40 |
41 |
42 | 62 | 63 | 70 |
71 |             
72 |
73 |
74 | 79 | 80 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/Extend/BeetleX.FastHttpApi.Admin/views/_admin/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/Extend/BeetleX.FastHttpApi.Admin/views/_admin/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/Extend/BeetleX.FastHttpApi.Admin/views/_admin/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Admin/views/_admin/md5.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";function b(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c}function c(a,b){return a<>>32-b}function d(a,d,e,f,g,h){return b(c(b(b(d,a),b(f,h)),g),e)}function e(a,b,c,e,f,g,h){return d(b&c|~b&e,a,b,f,g,h)}function f(a,b,c,e,f,g,h){return d(b&e|c&~e,a,b,f,g,h)}function g(a,b,c,e,f,g,h){return d(b^c^e,a,b,f,g,h)}function h(a,b,c,e,f,g,h){return d(c^(b|~e),a,b,f,g,h)}function i(a,c){a[c>>5]|=128<>>9<<4)+14]=c;var d,i,j,k,l,m=1732584193,n=-271733879,o=-1732584194,p=271733878;for(d=0;d>5]>>>b%32&255);return c}function k(a){var b,c=[];for(c[(a.length>>2)-1]=void 0,b=0;b>5]|=(255&a.charCodeAt(b/8))<16&&(e=i(e,8*a.length)),c=0;16>c;c+=1)f[c]=909522486^e[c],g[c]=1549556828^e[c];return d=i(f.concat(k(b)),512+8*b.length),j(i(g.concat(d),640))}function n(a){var b,c,d="0123456789abcdef",e="";for(c=0;c>>4&15)+d.charAt(15&b);return e}function o(a){return unescape(encodeURIComponent(a))}function p(a){return l(o(a))}function q(a){return n(p(a))}function r(a,b){return m(o(a),o(b))}function s(a,b){return n(r(a,b))}function t(a,b,c){return b?c?r(b,a):s(b,a):c?p(a):q(a)}"function"==typeof define&&define.amd?define(function(){return t}):a.md5=t}(this); -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Hosting/BeetleX.FastHttpApi.Hosting.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | henryfan 6 | ikende.com 7 | beetlex.fasthttpapi hosting and dependency injection extensions 8 | Copyright © ikende.com 2019 email:henryfan@msn.com 9 | https://github.com/IKende/FastHttpApi/blob/master/LICENSE 10 | https://github.com/IKende/FastHttpApi 11 | beetlex.fasthttpapi hosting and dependency injection extensions 12 | 0.8.2.0 13 | 0.8.2.0 14 | 0.8.2 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.Hosting/FastHttpApiHosting.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Hosting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace BeetleX.FastHttpApi.Hosting 11 | { 12 | 13 | public static class FastHttpApiExtension 14 | { 15 | public static IServiceCollection UseBeetlexHttp(this IServiceCollection service, Action action, params System.Reflection.Assembly[] assemblies) 16 | { 17 | HttpSettingHandler settingHandler = new HttpSettingHandler(); 18 | settingHandler.Assemblies = assemblies; 19 | settingHandler.Options = action; 20 | settingHandler.Services = service; 21 | service.AddSingleton(settingHandler); 22 | ServiceCollection services = new ServiceCollection(); 23 | return service.AddHostedService(); 24 | } 25 | } 26 | 27 | public class HttpSettingHandler 28 | { 29 | public Action Options { get; set; } 30 | 31 | public System.Reflection.Assembly[] Assemblies { get; set; } 32 | 33 | public IServiceCollection Services { get; set; } 34 | } 35 | 36 | public class HttpServer : IHostedService 37 | { 38 | public HttpServer(HttpSettingHandler httpSettingHandler) 39 | { 40 | mSettingHandler = httpSettingHandler; 41 | } 42 | 43 | private ServiceCollection mHttpControllerServices = new ServiceCollection(); 44 | 45 | private IServiceProvider mHttpControllerServiceProvider; 46 | 47 | private HttpSettingHandler mSettingHandler; 48 | 49 | private HttpApiServer mHttpServer; 50 | 51 | private void InitService() 52 | { 53 | ServiceDescriptor[] items = new ServiceDescriptor[mSettingHandler.Services.Count]; 54 | mSettingHandler.Services.CopyTo(items, 0); 55 | foreach (var item in items) 56 | { 57 | mHttpControllerServices.Insert(0, item); 58 | } 59 | foreach (Assembly item in mSettingHandler.Assemblies) 60 | { 61 | Type[] types = item.GetTypes(); 62 | foreach (Type type in types) 63 | { 64 | ControllerAttribute ca = type.GetCustomAttribute(false); 65 | if (ca != null) 66 | { 67 | if (ca.SingleInstance) 68 | { 69 | mHttpControllerServices.AddSingleton(type); 70 | } 71 | else 72 | { 73 | mHttpControllerServices.AddScoped(type); 74 | } 75 | } 76 | } 77 | } 78 | mHttpControllerServices.AddSingleton(mHttpServer); 79 | mHttpControllerServiceProvider = mHttpControllerServices.BuildServiceProvider(); 80 | if (mSettingHandler.Assemblies != null) 81 | mHttpServer.Register(mSettingHandler.Assemblies); 82 | } 83 | 84 | public virtual Task StartAsync(CancellationToken cancellationToken) 85 | { 86 | var option = HttpApiServer.LoadOptions(); 87 | mSettingHandler.Options(option); 88 | mHttpServer = new HttpApiServer(option); 89 | mHttpServer.ActionFactory.ControllerInstance += (o, e) => 90 | { 91 | e.Controller = mHttpControllerServiceProvider.GetService(e.Type); 92 | }; 93 | InitService(); 94 | mHttpServer.Open(); 95 | return Task.CompletedTask; 96 | } 97 | 98 | public virtual Task StopAsync(CancellationToken cancellationToken) 99 | { 100 | mHttpControllerServices.Clear(); 101 | mHttpServer.Dispose(); 102 | return Task.CompletedTask; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.SpanJson/BeetleX.FastHttpApi.SpanJson.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | Copyright © ikende.com 2019 email:henryfan@msn.com 6 | henryfan 7 | ikende.com 8 | SpanJson data result for FastHttpApi 9 | 10 | 0.5.2 11 | https://github.com/IKende/FastHttpApi 12 | https://github.com/IKende/FastHttpApi/blob/master/LICENSE 13 | 0.5.2 14 | 0.5.2 15 | 7.3 16 | BeetleX.FastHttpApi.SpanJson 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.SpanJson/BeetleX.FastHttpApi.SpanJson.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | <_LastSelectedProfileId>I:\VisualStudio\BitHub\FastHttpApi\Extend\BeetleX.FastHttpApi.SpanJson\Properties\PublishProfiles\FolderProfile.pubxml 5 | 6 | -------------------------------------------------------------------------------- /Extend/BeetleX.FastHttpApi.SpanJson/SpanJsonResult.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using SpanJson; 6 | namespace BeetleX.FastHttpApi.SpanJson 7 | { 8 | public class SpanJsonResult : ResultBase 9 | { 10 | public SpanJsonResult(object data) 11 | { 12 | Data = data; 13 | } 14 | 15 | public object Data { get; set; } 16 | 17 | public override IHeaderItem ContentType => ContentTypes.JSON; 18 | 19 | public override bool HasBody => true; 20 | 21 | public override void Write(PipeStream stream, HttpResponse response) 22 | { 23 | using (stream.LockFree()) 24 | { 25 | var task = JsonSerializer.NonGeneric.Utf8.SerializeAsync(Data, stream).AsTask(); 26 | task.Wait(); 27 | } 28 | } 29 | } 30 | 31 | public class SpanJsonResultFilter : FilterAttribute 32 | { 33 | public override void Executed(ActionContext context) 34 | { 35 | base.Executed(context); 36 | if (!(context.Result is IResult)) 37 | context.Result = new SpanJsonResult(context.Result); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/AspCoreWebapi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/AspCoreWebapi.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 600 5 | AspCoreWebapi 6 | FolderProfile 7 | 8 | 9 | ProjectDebugger 10 | 11 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Northwind.Data; 7 | 8 | namespace AspCoreWebapi.Controllers 9 | { 10 | public class HomeController : Controller 11 | { 12 | 13 | public class JsonMessage 14 | { 15 | public string message { get; set; } 16 | } 17 | 18 | public string plaintext() 19 | { 20 | return "Hello, World!"; 21 | } 22 | 23 | public object json() 24 | { 25 | return new JsonMessage { message = "Hello, World!" }; 26 | } 27 | 28 | public List Employees() 29 | { 30 | return DataHelper.Defalut.Employees; 31 | } 32 | 33 | public Employee Employee(int id) 34 | { 35 | Employee result = DataHelper.Defalut.Employees.Find(e => e.EmployeeID == id); 36 | if (result == null) 37 | result = new Employee(); 38 | return result; 39 | } 40 | 41 | public object Orders(int id, string customerid, int index, int size) 42 | { 43 | Func exp = o => (id == 0 || o.EmployeeID == id) 44 | && (string.IsNullOrEmpty(customerid) || o.CustomerID == customerid); 45 | int count = DataHelper.Defalut.Orders.Count(exp); 46 | if (size == 0) 47 | size = 10; 48 | int pages = count / size; 49 | if (count % size > 0) 50 | pages++; 51 | var items = DataHelper.Defalut.Orders.Where(exp).Skip(index * size).Take(size); 52 | return items; 53 | 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace AspCoreWebapi 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | CreateWebHostBuilder(args).Build().Run(); 19 | } 20 | 21 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 22 | WebHost.CreateDefaultBuilder(args) 23 | .UseStartup().UseKestrel(opt => { opt.Listen(IPAddress.Parse("0.0.0.0"), 8080); }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | using Microsoft.Extensions.Options; 12 | 13 | namespace AspCoreWebapi 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 28 | } 29 | 30 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 31 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 32 | { 33 | if (env.IsDevelopment()) 34 | { 35 | app.UseDeveloperExceptionPage(); 36 | } 37 | app.UseMvc(routes => 38 | { 39 | routes.MapRoute( 40 | name: "default", 41 | template: "{controller=Home}/{action=Index}/{id?}"); 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/AspCoreWebapi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/BeetlexWebapi/BeetlexWebapi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/BeetlexWebapi/BeetlexWebapi.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | <_LastSelectedProfileId>I:\VisualStudio\BitHub\FastHttpApi\PerformanceTest\Beetlex_VS_AspCore_webapi\BeetlexWebapi\Properties\PublishProfiles\FolderProfile.pubxml 5 | 6 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/BeetlexWebapi/Program.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.FastHttpApi; 2 | using BeetleX.FastHttpApi.SpanJson; 3 | using Northwind.Data; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace BeetlexWebapi 9 | { 10 | class Program 11 | { 12 | private static HttpApiServer mApiServer; 13 | static void Main(string[] args) 14 | { 15 | mApiServer = new HttpApiServer(); 16 | mApiServer.Register(typeof(Program).Assembly);//加载程序集中所有控制器信息和静态资源信息 17 | mApiServer.Options.Port = 9090; 18 | mApiServer.Options.LogLevel = BeetleX.EventArgs.LogType.Warring; 19 | mApiServer.Options.LogToConsole = true; 20 | mApiServer.Debug();//只有在Debug模式下生产,把静态资源加载目录指向项目的views目录 21 | mApiServer.Open(); 22 | Console.Write(mApiServer.BaseServer); 23 | Console.Read(); 24 | } 25 | } 26 | [Controller(BaseUrl = "Home")] 27 | public class Controller 28 | { 29 | public class JsonMessage 30 | { 31 | public string message { get; set; } 32 | } 33 | 34 | public object plaintext() 35 | { 36 | return new TextResult("Hello, World!"); 37 | } 38 | 39 | public object json() 40 | { 41 | return new JsonResult(new JsonMessage { message = "Hello, World!" }); 42 | } 43 | 44 | public List Employees() 45 | { 46 | return DataHelper.Defalut.Employees; 47 | } 48 | [SpanJsonResultFilter] 49 | public List EmployeesSpan() 50 | { 51 | return DataHelper.Defalut.Employees; 52 | } 53 | 54 | [Get(Route = "{id}")] 55 | public object Employee(int id) 56 | { 57 | Employee result = DataHelper.Defalut.Employees.Find(e => e.EmployeeID == id); 58 | if (result == null) 59 | result = new Employee(); 60 | return new JsonResult(result); 61 | } 62 | [Get(Route = "{id}")] 63 | public object Orders(int id, string customerid, int index, int size) 64 | { 65 | Func exp = o => (id == 0 || o.EmployeeID == id) 66 | && (string.IsNullOrEmpty(customerid) || o.CustomerID == customerid); 67 | int count = DataHelper.Defalut.Orders.Count(exp); 68 | if (size == 0) 69 | size = 10; 70 | int pages = count / size; 71 | if (count % size > 0) 72 | pages++; 73 | var items = DataHelper.Defalut.Orders.Where(exp).Skip(index * size).Take(size); 74 | return new JsonResult(items); 75 | 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /PerformanceTest/Beetlex_VS_AspCore_webapi/Beetlex_VS_AspCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspCoreWebapi", "AspCoreWebapi\AspCoreWebapi.csproj", "{02AEEE91-1E5D-4998-9F71-835FE23670E4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeetlexWebapi", "BeetlexWebapi\BeetlexWebapi.csproj", "{49EDDFEE-2F42-4801-98A1-2D3886BAB83C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {02AEEE91-1E5D-4998-9F71-835FE23670E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {02AEEE91-1E5D-4998-9F71-835FE23670E4}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {02AEEE91-1E5D-4998-9F71-835FE23670E4}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {02AEEE91-1E5D-4998-9F71-835FE23670E4}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {49EDDFEE-2F42-4801-98A1-2D3886BAB83C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {49EDDFEE-2F42-4801-98A1-2D3886BAB83C}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {49EDDFEE-2F42-4801-98A1-2D3886BAB83C}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {49EDDFEE-2F42-4801-98A1-2D3886BAB83C}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {CDBF3793-0391-43FA-A272-27A443E09B6D} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /PerformanceTest/FastHttpApiWeb/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PerformanceTest/FastHttpApiWeb/FastHttpApiWeb.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Always 16 | 17 | 18 | Always 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /PerformanceTest/FastHttpApiWeb/FastHttpApiWeb.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | <_LastSelectedProfileId>I:\VisualStudio\BitHub\FastHttpApi\PerformanceTest\FastHttpApiWeb\Properties\PublishProfiles\FolderProfile.pubxml 5 | 6 | -------------------------------------------------------------------------------- /PerformanceTest/FastHttpApiWeb/HttpConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "HttpConfig": { 3 | "Host": "", 4 | "Port": 80, 5 | "SSL": false, 6 | "CertificateFile": "", 7 | "CertificatePassword": "", 8 | "MaxBodyLength": 2097152, 9 | "OutputStackTrace": false, 10 | "StaticResurceType": "xml;svg;woff;woff2;jpg;jpeg;gif;png;js;html;htm;css;txt;ico;zip;rar", 11 | "DefaultPage": "index.html;index.htm", 12 | "NotLoadFolder": "\\Files;\\Images;\\Data", 13 | "Manager": "admin", 14 | "ManagerPWD": "henry-13579", 15 | "NoGzipFiles": "jpg;jpeg;png;gif;png;ico;zip;rar;bmp", 16 | "CacheFiles": "html;htm;js;css", 17 | "BufferSize": 8192, 18 | "WebSocketMaxRPS": 2000, 19 | "WriteLog": true, 20 | "LogToConsole": true, 21 | "UrlIgnoreCase": false, 22 | "LogLevel": "Warring", 23 | "FileManager": true 24 | } 25 | } -------------------------------------------------------------------------------- /PerformanceTest/FastHttpApiWeb/Program.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.FastHttpApi; 2 | using System; 3 | 4 | namespace FastHttpApiWeb 5 | { 6 | [Controller(BaseUrl = "api")] 7 | public class Program 8 | { 9 | private static HttpApiServer mApiServer; 10 | 11 | static void Main(string[] args) 12 | { 13 | mApiServer = new HttpApiServer(); 14 | 15 | mApiServer.Register(typeof(Program).Assembly); 16 | mApiServer.Open(); 17 | 18 | Console.Write(mApiServer.BaseServer); 19 | Console.Read(); 20 | } 21 | [Get(Route = "{name}")] 22 | public object values(string name) 23 | { 24 | return new TextResult("sdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PerformanceTest/go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/kataras/iris" 4 | 5 | func main() { 6 | app := iris.New() 7 | app.Get("/api/values/{id}", func(ctx iris.Context) { 8 | ctx.WriteString("value") 9 | }) 10 | 11 | app.Run(iris.Addr(":8080")) 12 | } 13 | -------------------------------------------------------------------------------- /PerformanceTest/netcore-mvc/.vs/netcore-mvc/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/PerformanceTest/netcore-mvc/.vs/netcore-mvc/v15/.suo -------------------------------------------------------------------------------- /PerformanceTest/netcore-mvc/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | 4 | namespace netcore_mvc.Controllers 5 | { 6 | // ValuesController is the equivalent 7 | // `ValuesController` of the Iris 8.3 mvc application. 8 | [Route("api/Values")] 9 | public class ValuesController : ControllerBase 10 | { 11 | // Get handles "GET" requests to "api/values/{id}". 12 | [HttpGet("{id}")] 13 | public string Get(string id) 14 | { 15 | return id; 16 | } 17 | 18 | // Put handles "PUT" requests to "api/values/{id}". 19 | [HttpPost("{id}")] 20 | public void Post() 21 | { 22 | } 23 | 24 | // Delete handles "DELETE" requests to "api/values/{id}". 25 | [HttpDelete("{id}")] 26 | public void Delete() 27 | { 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /PerformanceTest/netcore-mvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace netcore_mvc 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | BuildWebHost(args).Run(); 19 | } 20 | 21 | public static IWebHost BuildWebHost(string[] args) => 22 | new WebHostBuilder().UseKestrel() 23 | .UseStartup().UseKestrel(opt => { opt.Listen(IPAddress.Parse("0.0.0.0"), 8008); }) 24 | .Build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PerformanceTest/netcore-mvc/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace netcore_mvc 7 | { 8 | public class Startup 9 | { 10 | public Startup(IConfiguration configuration) 11 | { 12 | Configuration = configuration; 13 | } 14 | 15 | public IConfiguration Configuration { get; } 16 | 17 | // This method gets called by the runtime. Use this method to add services to the container. 18 | public void ConfigureServices(IServiceCollection services) 19 | { 20 | services.AddMvcCore(); 21 | } 22 | 23 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 24 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 25 | { 26 | app.UseMvc(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /PerformanceTest/netcore-mvc/netcore-mvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /PerformanceTest/netcore-mvc/netcore-mvc.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcore-mvc 5 | FolderProfile 6 | 7 | 8 | ProjectDebugger 9 | 10 | -------------------------------------------------------------------------------- /PerformanceTest/netcore/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace netcore 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | BuildWebHost(args).Run(); 19 | } 20 | 21 | public static IWebHost BuildWebHost(string[] args) => 22 | new WebHostBuilder().UseKestrel() 23 | .UseStartup().UseKestrel(opt => { opt.Listen(IPAddress.Parse("0.0.0.0"), 8007); }) 24 | .Build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PerformanceTest/netcore/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.AspNetCore.Routing; 10 | using Microsoft.AspNetCore.Http; 11 | 12 | namespace netcore 13 | { 14 | public class Startup 15 | { 16 | public Startup(IConfiguration configuration) 17 | { 18 | Configuration = configuration; 19 | } 20 | 21 | public IConfiguration Configuration { get; } 22 | 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | services.AddRouting(); 26 | } 27 | 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | var routeBuilder = new RouteBuilder(app); 31 | routeBuilder.MapGet("api/values/{id}", context => 32 | { 33 | var id = "sdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffhenrysdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; 34 | 35 | return context.Response.WriteAsync(id); 36 | }); 37 | var routes = routeBuilder.Build(); 38 | app.UseRouter(routes); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PerformanceTest/netcore/netcore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /PerformanceTest/netcore/netcore.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcore 5 | FolderProfile 6 | 7 | 8 | ProjectDebugger 9 | 10 | -------------------------------------------------------------------------------- /WebApi_json_vs_protobuf.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/WebApi_json_vs_protobuf.zip -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /src/ActionFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 8 | public class FilterAttribute : Attribute 9 | { 10 | public virtual void Init(IHttpContext context, ActionHandler handler) 11 | { 12 | 13 | } 14 | 15 | public virtual bool Executing(ActionContext context) 16 | { 17 | return true; 18 | } 19 | 20 | public virtual void Executed(ActionContext context) 21 | { 22 | 23 | } 24 | 25 | public virtual void Disposed(ActionContext context) 26 | { 27 | 28 | } 29 | } 30 | 31 | public class DefaultJsonResultFilter : FilterAttribute 32 | { 33 | public override void Executed(ActionContext context) 34 | { 35 | base.Executed(context); 36 | if (!(context.Result is IResult)) 37 | context.Result = new JsonResult(context.Result); 38 | } 39 | } 40 | [AttributeUsage(AttributeTargets.Class)] 41 | public class FilterRouteAttribute : Attribute 42 | { 43 | public FilterRouteAttribute(string url) 44 | { 45 | Url = url; 46 | } 47 | public string Url { get; set; } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/ActionResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class ActionResult 8 | { 9 | public ActionResult() 10 | { 11 | Code = 200; 12 | } 13 | 14 | public ActionResult(object data) 15 | { 16 | Code = 200; 17 | Data = data; 18 | } 19 | 20 | public ActionResult(int code, string error) 21 | { 22 | Code = code; 23 | Error = error; 24 | } 25 | 26 | public string Url { get; set; } 27 | 28 | public string Error { get; set; } 29 | 30 | public int Code { get; set; } 31 | 32 | public string StackTrace { get; set; } 33 | 34 | public object Data { get; set; } 35 | 36 | public string ID { get; set; } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ActionSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class ActionSettings 8 | { 9 | const string SETTING_FILE = "action_settings.json"; 10 | 11 | public class ActionInfo 12 | { 13 | public ActionInfo() 14 | { 15 | 16 | } 17 | public ActionInfo(ActionHandler handler) 18 | { 19 | Url = handler.Url; 20 | MaxRps = handler.MaxRPS; 21 | if (handler.ThreadQueue != null) 22 | ThreadInfo = ThreadInfo.GetThreadInfo(handler.ThreadQueue); 23 | else 24 | ThreadInfo = new ThreadInfo(); 25 | } 26 | public string Url { get; set; } 27 | 28 | public int MaxRps { get; set; } 29 | 30 | public ThreadInfo ThreadInfo { get; set; } 31 | 32 | public void SetTo(ActionHandler handler) 33 | { 34 | 35 | handler.MaxRPS = MaxRps; 36 | if (ThreadInfo != null) 37 | { 38 | if (ThreadInfo.Type == ThreadQueueType.None.ToString()) 39 | handler.ThreadQueue = null; 40 | else 41 | handler.ThreadQueue = ThreadInfo?.GetThreadQueue(); 42 | } 43 | } 44 | public override string ToString() 45 | { 46 | return $"{Url} {MaxRps} {ThreadInfo}"; 47 | } 48 | } 49 | 50 | public class ThreadInfo 51 | { 52 | public string Type { get; set; } = "None"; 53 | 54 | public int Count { get; set; } = 1; 55 | 56 | public string UniqueName { get; set; } 57 | 58 | public ThreadQueueAttribute GetThreadQueue() 59 | { 60 | return GetThreadQueue(this); 61 | } 62 | public static ThreadQueueAttribute GetThreadQueue(ThreadInfo info) 63 | { 64 | ThreadQueueType type = Enum.Parse(info.Type == null ? "None" : info.Type); 65 | if (type == ThreadQueueType.Single) 66 | return new ThreadQueueAttribute(ThreadQueueType.Single); 67 | else if (type == ThreadQueueType.Multiple) 68 | return new ThreadQueueAttribute(ThreadQueueType.Multiple, info.Count); 69 | else if (type == ThreadQueueType.DataUnique) 70 | return new ThreadQueueAttribute(info.UniqueName); 71 | return null; 72 | 73 | } 74 | public static ThreadInfo GetThreadInfo(ActionHandler handler) 75 | { 76 | if (handler.ThreadQueue != null) 77 | return GetThreadInfo(handler.ThreadQueue); 78 | else 79 | return new ThreadInfo { Type = "None" }; 80 | } 81 | public static ThreadInfo GetThreadInfo(ThreadQueueAttribute threadQueue) 82 | { 83 | ThreadInfo info = new ThreadInfo(); 84 | info.Type = threadQueue.Type.ToString(); 85 | info.Count = threadQueue.Count; 86 | info.UniqueName = threadQueue.UniqueName; 87 | return info; 88 | } 89 | 90 | public override string ToString() 91 | { 92 | return $"{Type.ToString()}[{Count}|{UniqueName}]"; 93 | } 94 | } 95 | 96 | public void SetAction(ActionHandler handler) 97 | { 98 | var item = Settings.Find(i => i.Url.ToLower() == handler.Url.ToLower()); 99 | if (item != null) 100 | item.SetTo(handler); 101 | } 102 | 103 | public List Settings { get; set; } = new List(); 104 | 105 | public void Save(params ActionHandler[] actions) 106 | { 107 | using (System.IO.StreamWriter writer = new System.IO.StreamWriter(SETTING_FILE, false)) 108 | { 109 | this.Settings.Clear(); 110 | foreach (var item in actions) 111 | this.Settings.Add(new ActionInfo(item)); 112 | string value = Newtonsoft.Json.JsonConvert.SerializeObject(this.Settings); 113 | writer.Write(value); 114 | writer.Flush(); 115 | } 116 | } 117 | 118 | public void Load() 119 | { 120 | if (System.IO.File.Exists(SETTING_FILE)) 121 | { 122 | using (System.IO.StreamReader reader = new System.IO.StreamReader(SETTING_FILE)) 123 | { 124 | string value = reader.ReadToEnd(); 125 | Settings = Newtonsoft.Json.JsonConvert.DeserializeObject>(value); 126 | } 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/AuthMarkAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 8 | public class AuthMarkAttribute : Attribute 9 | { 10 | public AuthMarkAttribute(AuthMarkType type) 11 | { 12 | Type = type; 13 | } 14 | public AuthMarkType Type { get; set; } 15 | 16 | public string Token { get; set; } 17 | 18 | public virtual string GetTokenValue(IHttpContext context) 19 | { 20 | return Token; 21 | } 22 | } 23 | public enum AuthMarkType 24 | { 25 | None = 1, 26 | User = 2, 27 | Group = 4, 28 | Manager = 8, 29 | Admin = 16, 30 | System = 32, 31 | NoValidation = 1024 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/AutoLoaderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] 8 | public class AssemblyAutoLoaderAttribute : Attribute 9 | { 10 | public AssemblyAutoLoaderAttribute(string id) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/BeetleX.FastHttpApi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1;netcoreapp3.1;net5.0;net6.0 5 | Copyright © beetlex-io.com 2019-2022 email: admin@beetlex-io.com or henryfan@msn.com 6 | henryfan 7 | beetlex.io 8 | high performance and lightweight http and websocket server components for .NETCore 9 | 2.2.24.0425 10 | https://github.com/beetlex-io/FastHttpApi 11 | 12 | 2.2.24.0425 13 | 2.2.24.0425 14 | 7.3 15 | BeetleX.FastHttpApi 16 | high performance and lightweight http and websocket server components for .NETCore 17 | False 18 | Beetlex.FastHttpApi.pfx 19 | false 20 | 21 | 22 | E:\public 23 | 24 | 25 | 26 | true 27 | 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/BeetleX.FastHttpApi.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | <_LastSelectedProfileId>D:\beetlexproject\BeetleX\BeetleX\BeetleX.FastHttpAPI\Properties\PublishProfiles\FolderProfile.pubxml 5 | false 6 | 7 | -------------------------------------------------------------------------------- /src/Beetlex.FastHttpApi.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beetlex-io/FastHttpApi/cb08994ae14e9be9de1f83fb3d23942c7bbd6214/src/Beetlex.FastHttpApi.pfx -------------------------------------------------------------------------------- /src/Clients/HttpApiBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BeetleX.FastHttpApi.Clients 8 | { 9 | 10 | public class HttpApiProxy : System.Reflection.DispatchProxy 11 | { 12 | public HttpApiProxy() 13 | { 14 | TimeOut = 10000; 15 | } 16 | 17 | public HttpHost Host { get; set; } 18 | 19 | public int TimeOut { get; set; } 20 | 21 | protected override object Invoke(MethodInfo targetMethod, object[] args) 22 | { 23 | ClientActionHanler handler = ClientActionFactory.GetHandler((MethodInfo)targetMethod); 24 | var rinfo = handler.GetRequestInfo(args); 25 | var request = rinfo.GetRequest(Host); 26 | var task = request.Execute(); 27 | if (!handler.Async) 28 | { 29 | task.Wait(TimeOut); 30 | if (!task.Wait(TimeOut)) 31 | { 32 | throw new HttpClientException(request, Host.Uri, $"{rinfo.Method} {rinfo.Url} request time out!"); 33 | } 34 | if (task.Result.Exception != null) 35 | throw task.Result.Exception; 36 | return task.Result.Body; 37 | } 38 | else 39 | { 40 | if (handler.MethodType == typeof(ValueTask)) 41 | { 42 | AnyCompletionSource source = new AnyCompletionSource(); 43 | source.WaitResponse(task); 44 | return new ValueTask(source.Task); 45 | } 46 | else 47 | { 48 | Type gtype = typeof(AnyCompletionSource<>); 49 | Type type = gtype.MakeGenericType(handler.ReturnType); 50 | IAnyCompletionSource source = (IAnyCompletionSource)Activator.CreateInstance(type); 51 | source.WaitResponse(task); 52 | return source.GetTask(); 53 | } 54 | } 55 | } 56 | } 57 | 58 | interface IAnyCompletionSource 59 | { 60 | void Success(object data); 61 | void Error(Exception error); 62 | void WaitResponse(Task task); 63 | Task GetTask(); 64 | } 65 | 66 | class AnyCompletionSource : TaskCompletionSource, IAnyCompletionSource 67 | { 68 | public void Success(object data) 69 | { 70 | TrySetResult((T)data); 71 | } 72 | 73 | public void Error(Exception error) 74 | { 75 | TrySetException(error); 76 | } 77 | 78 | public async void WaitResponse(Task task) 79 | { 80 | var response = await task; 81 | if (response.Exception != null) 82 | Error(response.Exception); 83 | else 84 | Success(response.Body); 85 | } 86 | 87 | public Task GetTask() 88 | { 89 | return this.Task; 90 | } 91 | } 92 | 93 | public class HttpApiClient 94 | { 95 | public HttpApiClient(string host) 96 | { 97 | Host = new HttpHost(host); 98 | } 99 | 100 | public HttpHost Host { get; set; } 101 | 102 | protected async Task OnExecute(MethodBase targetMethod, params object[] args) 103 | { 104 | var rinfo = ClientActionFactory.GetHandler((MethodInfo)targetMethod).GetRequestInfo(args); 105 | var request = rinfo.GetRequest(Host); 106 | var respnse = await request.Execute(); 107 | if (respnse.Exception != null) 108 | throw respnse.Exception; 109 | return (T)respnse.Body; 110 | } 111 | 112 | private System.Collections.Concurrent.ConcurrentDictionary mAPI = new System.Collections.Concurrent.ConcurrentDictionary(); 113 | 114 | public T Create() 115 | { 116 | Type type = typeof(T); 117 | object result; 118 | if (!mAPI.TryGetValue(type, out result)) 119 | { 120 | result = DispatchProxy.Create(); 121 | mAPI[type] = result; 122 | ((HttpApiProxy)result).Host = Host; 123 | } 124 | return (T)result; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Clients/HttpClientException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.Clients 6 | { 7 | public class HttpClientException : Exception 8 | { 9 | public HttpClientException(Request request, Uri host, string message, Exception innerError = null) : base($"request {host} error {message}", innerError) 10 | { 11 | Request = request; 12 | Host = host; 13 | SocketError = false; 14 | if (innerError != null && (innerError is System.Net.Sockets.SocketException || innerError is ObjectDisposedException)) 15 | { 16 | SocketError = true; 17 | } 18 | } 19 | public Uri Host { get; set; } 20 | 21 | public Request Request { get; set; } 22 | 23 | public bool SocketError { get; internal set; } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Clients/IBodyFormater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using BeetleX.Buffers; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace BeetleX.FastHttpApi.Clients 11 | { 12 | public interface IClientBodyFormater 13 | { 14 | string ContentType { get; } 15 | 16 | void Serialization(object data, PipeStream stream); 17 | 18 | object Deserialization(BeetleX.Buffers.PipeStream stream, Type type, int length); 19 | } 20 | 21 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Class)] 22 | public abstract class FormaterAttribute : Attribute, IClientBodyFormater 23 | { 24 | public abstract string ContentType { get; } 25 | 26 | public abstract void Serialization(object data, PipeStream stream); 27 | 28 | public abstract object Deserialization(BeetleX.Buffers.PipeStream stream, Type type, int length); 29 | } 30 | 31 | public class FormUrlFormater : FormaterAttribute 32 | { 33 | public override string ContentType => "application/x-www-form-urlencoded"; 34 | 35 | public override object Deserialization(PipeStream stream, Type type, int length) 36 | { 37 | return stream.ReadString(length); 38 | } 39 | public override void Serialization(object data, PipeStream stream) 40 | { 41 | System.Collections.IDictionary keyValuePairs = data as IDictionary; 42 | if (keyValuePairs != null) 43 | { 44 | int i = 0; 45 | foreach (object key in keyValuePairs.Keys) 46 | { 47 | object value = keyValuePairs[key]; 48 | if (value != null) 49 | { 50 | if (i > 0) 51 | stream.Write("&"); 52 | stream.Write(key.ToString() + "="); 53 | if (value is string) 54 | { 55 | stream.Write(System.Net.WebUtility.UrlEncode((string)value)); 56 | } 57 | else 58 | { 59 | stream.Write(System.Net.WebUtility.UrlEncode(value.ToString())); 60 | } 61 | i++; 62 | } 63 | } 64 | } 65 | else 66 | { 67 | stream.Write(data.ToString()); 68 | } 69 | } 70 | } 71 | 72 | public class JsonFormater : FormaterAttribute 73 | { 74 | public override string ContentType => "application/json"; 75 | 76 | public override object Deserialization(PipeStream stream, Type type, int length) 77 | { 78 | using (stream.LockFree()) 79 | { 80 | if (type == null) 81 | { 82 | using (System.IO.StreamReader streamReader = new System.IO.StreamReader(stream)) 83 | using (JsonTextReader reader = new JsonTextReader(streamReader)) 84 | { 85 | JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(); 86 | object token = jsonSerializer.Deserialize(reader); 87 | return token; 88 | } 89 | } 90 | else 91 | { 92 | using (StreamReader streamReader = new StreamReader(stream)) 93 | { 94 | JsonSerializer serializer = new JsonSerializer(); 95 | object result = serializer.Deserialize(streamReader, type); 96 | return result; 97 | } 98 | } 99 | } 100 | } 101 | 102 | public override void Serialization(object data, PipeStream stream) 103 | { 104 | using (stream.LockFree()) 105 | { 106 | using (StreamWriter writer = new StreamWriter(stream)) 107 | { 108 | IDictionary dictionary = data as IDictionary; 109 | JsonSerializer serializer = new JsonSerializer(); 110 | if (dictionary != null && dictionary.Count == 1) 111 | { 112 | object[] vlaues = new object[dictionary.Count]; 113 | dictionary.Values.CopyTo(vlaues, 0); 114 | serializer.Serialize(writer, vlaues[0]); 115 | } 116 | else 117 | { 118 | serializer.Serialize(writer, data); 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Clients/INodeSourcesHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace BeetleX.FastHttpApi.Clients 7 | { 8 | public class NodeSource 9 | { 10 | public string Url { get; set; } 11 | 12 | public IApiNode Node { get; set; } 13 | } 14 | 15 | public interface INodeSourceHandler 16 | { 17 | int UpdateTime { get; set; } 18 | Task Load(); 19 | } 20 | 21 | [JsonFormater] 22 | public interface IHttpSourceApi 23 | { 24 | [Get] 25 | Task _GetCluster(string cluster = "default"); 26 | } 27 | 28 | public class HTTPRemoteSourceHandler : INodeSourceHandler 29 | { 30 | private HttpClusterApi mHttpClusterApi = new HttpClusterApi(); 31 | 32 | private IHttpSourceApi mRemoteSourceApi; 33 | 34 | public HTTPRemoteSourceHandler(string name, params string[] hosts) 35 | { 36 | mHttpClusterApi.AddHost("*", hosts); 37 | mRemoteSourceApi = mHttpClusterApi.Create(); 38 | Name = name; 39 | } 40 | 41 | public string Name { get; set; } 42 | 43 | public int UpdateTime { get; set; } = 5; 44 | 45 | public Task Load() 46 | { 47 | return mRemoteSourceApi._GetCluster(Name); 48 | } 49 | } 50 | 51 | 52 | public class ApiClusterInfo 53 | { 54 | public ApiClusterInfo() 55 | { 56 | Urls = new List(); 57 | } 58 | 59 | public string Name { get; set; } 60 | 61 | public string Version { get; set; } 62 | 63 | public List Urls { get; set; } 64 | } 65 | 66 | public class UrlNodeInfo 67 | { 68 | 69 | public UrlNodeInfo() 70 | { 71 | Hosts = new List(); 72 | } 73 | 74 | public string Name { get; set; } 75 | 76 | public List Hosts { get; set; } 77 | 78 | public ApiNode GetNode() 79 | { 80 | ApiNode result = new ApiNode(Name); 81 | foreach (var item in Hosts) 82 | { 83 | result.Add(item.Name, item.Weight); 84 | } 85 | return result; 86 | 87 | } 88 | } 89 | 90 | public class UrlHostInfo 91 | { 92 | public string Name { get; set; } 93 | 94 | public int Weight { get; set; } 95 | 96 | public int MaxRPS { get; set; } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Clients/Response.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | using System.Text; 6 | 7 | namespace BeetleX.FastHttpApi.Clients 8 | { 9 | public class Response 10 | { 11 | 12 | public Response() 13 | { 14 | Header = new Header(); 15 | Cookies = new Cookies(); 16 | mState = LoadedState.None; 17 | this.KeepAlive = true; 18 | } 19 | 20 | public HttpClientException Exception { get; set; } 21 | 22 | public Cookies Cookies { get; private set; } 23 | 24 | public string Code { get; set; } 25 | 26 | public string CodeMsg { get; set; } 27 | 28 | public bool KeepAlive { get; set; } 29 | 30 | public string HttpVersion { get; set; } 31 | 32 | public object Body { get; set; } 33 | 34 | public int Length { get; set; } 35 | 36 | public Header Header { get; private set; } 37 | 38 | public bool Chunked { get; set; } 39 | 40 | public bool Gzip { get; set; } 41 | 42 | internal PipeStream Stream { get; set; } 43 | 44 | private LoadedState mState; 45 | 46 | public LoadedState Load(PipeStream stream) 47 | { 48 | string line; 49 | if (mState == LoadedState.None) 50 | { 51 | if (stream.TryReadWith(HeaderTypeFactory.LINE_BYTES, out line)) 52 | { 53 | HttpParse.AnalyzeResponseLine(line, this); 54 | mState = LoadedState.Method; 55 | } 56 | } 57 | if (mState == LoadedState.Method) 58 | { 59 | if (Header.Read(stream, Cookies)) 60 | { 61 | mState = LoadedState.Header; 62 | } 63 | } 64 | if (mState == LoadedState.Header) 65 | { 66 | if (string.Compare(Header[HeaderTypeFactory.CONNECTION], "close", true) == 0) 67 | { 68 | this.KeepAlive = false; 69 | } 70 | if (string.Compare(Header[HeaderTypeFactory.TRANSFER_ENCODING], "chunked", true) == 0) 71 | { 72 | Chunked = true; 73 | } 74 | else 75 | { 76 | string lenstr = Header[HeaderTypeFactory.CONTENT_LENGTH]; 77 | int length = 0; 78 | if (lenstr != null) 79 | int.TryParse(lenstr, out length); 80 | Length = length; 81 | } 82 | mState = LoadedState.Completed; 83 | } 84 | return mState; 85 | } 86 | 87 | 88 | [ThreadStatic] 89 | private static Response mCurrent; 90 | public static Response Current 91 | { 92 | get 93 | { 94 | return mCurrent; 95 | } 96 | set 97 | { 98 | mCurrent = value; 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/CommandLineArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | class CommandLineArgs 8 | { 9 | [Option("-host", Required = false)] 10 | public string Host { get; set; } 11 | [Option("-port", Required = false)] 12 | public int Port { get; set; } 13 | [Option("-sslport", Required = false)] 14 | public int SSLPort { get; set; } 15 | 16 | [Option("-sslfile", Required = false)] 17 | public string SSLFile { get; set; } 18 | 19 | [Option("-sslpwd", Required = false)] 20 | public string SSLPassWord { get; set; } 21 | 22 | [Option("-sock", Required = false)] 23 | public string Sock { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ConfigBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class ConfigBase where T : new() 8 | { 9 | 10 | public void Save() 11 | { 12 | string filename = typeof(T).Name + ".json"; 13 | string config = Newtonsoft.Json.JsonConvert.SerializeObject(this); 14 | lock (this) 15 | { 16 | using (System.IO.StreamWriter writer = new System.IO.StreamWriter(filename, false, Encoding.UTF8)) 17 | { 18 | writer.Write(config); 19 | writer.Flush(); 20 | } 21 | } 22 | } 23 | 24 | private static T mInstance = default(T); 25 | 26 | public static T Instance 27 | { 28 | get 29 | { 30 | if (mInstance == null) 31 | { 32 | string filename = typeof(T).Name + ".json"; 33 | if (System.IO.File.Exists(filename)) 34 | { 35 | using (System.IO.StreamReader reader = new System.IO.StreamReader(filename, Encoding.UTF8)) 36 | { 37 | string value = reader.ReadToEnd(); 38 | mInstance = Newtonsoft.Json.JsonConvert.DeserializeObject(value); 39 | } 40 | } 41 | else 42 | { 43 | mInstance = new T(); 44 | } 45 | } 46 | return mInstance; 47 | } 48 | } 49 | 50 | public T GetInstance() 51 | { 52 | return Instance; 53 | } 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ControllerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] 8 | public class ControllerAttribute : Attribute 9 | { 10 | public ControllerAttribute() 11 | { 12 | InstanceType = InstanceType.Single; 13 | } 14 | public string BaseUrl { get; set; } 15 | 16 | public InstanceType InstanceType { get; set; } 17 | 18 | public string ActorTag { get; set; } 19 | 20 | public bool SkipPublicFilter { get; set; } = false; 21 | } 22 | 23 | 24 | public enum InstanceType 25 | { 26 | Single, 27 | Session, 28 | None 29 | } 30 | 31 | 32 | public interface IBodyFlag { } 33 | 34 | public interface IController 35 | { 36 | void Init(HttpApiServer server, string path); 37 | } 38 | 39 | [AttributeUsage(AttributeTargets.Method)] 40 | public class NotActionAttribute : Attribute 41 | { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Cookies.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class Cookies 8 | { 9 | private Dictionary mItems = new Dictionary(); 10 | 11 | public string this[string name] 12 | { 13 | get 14 | { 15 | return GetValue(name); 16 | } 17 | } 18 | 19 | public void Clear() 20 | { 21 | mItems.Clear(); 22 | } 23 | 24 | public IDictionary Copy() 25 | { 26 | Dictionary result = new Dictionary(); 27 | foreach (var item in mItems) 28 | result[item.Key] = item.Value; 29 | return result; 30 | } 31 | 32 | private string GetValue(string name) 33 | { 34 | string result = null; 35 | mItems.TryGetValue(name, out result); 36 | return result; 37 | } 38 | 39 | internal void Add(string name, string value) 40 | { 41 | name = System.Web.HttpUtility.UrlDecode(name); 42 | value = System.Web.HttpUtility.UrlDecode(value); 43 | mItems[name] = value; 44 | } 45 | public override string ToString() 46 | { 47 | StringBuilder sb = new StringBuilder(); 48 | foreach (var item in mItems) 49 | { 50 | sb.AppendFormat("{0}={1}\r\n", item.Key, item.Value); 51 | } 52 | return sb.ToString(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/DatBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | public class DataBuffer : IDisposable 9 | { 10 | public DataBuffer(int length) 11 | { 12 | Length = length; 13 | Data = ArrayPool.Shared.Rent(length); 14 | } 15 | 16 | public T[] Data { get; set; } 17 | 18 | public int Length { get; set; } 19 | 20 | public int Offset { get; set; } 21 | 22 | public void Dispose() 23 | { 24 | ArrayPool.Shared.Return(Data); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Data/DataContextBinder.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace BeetleX.FastHttpApi.Data 7 | { 8 | class DataContextBind 9 | { 10 | public static void BindJson(IDataContext context, JToken data) 11 | { 12 | if (data != null) 13 | { 14 | context.SetValue("body", data); 15 | if (data is JObject) 16 | { 17 | foreach (JProperty property in data) 18 | { 19 | if (property.Value != null) 20 | { 21 | context.SetValue(property.Name, property); 22 | } 23 | } 24 | } 25 | } 26 | } 27 | 28 | public static void BindFormUrl(IDataContext context, ReadOnlySpan data) 29 | { 30 | HttpParse.AsynczeFromUrlEncoded(data, context); 31 | } 32 | 33 | public static DataConvertAttribute GetConvertAttribute(string contentType) 34 | { 35 | if (string.IsNullOrEmpty(contentType)) 36 | { 37 | return new JsonDataConvertAttribute(); 38 | } 39 | else if (contentType.IndexOf("application/x-www-form-urlencoded", StringComparison.CurrentCultureIgnoreCase) >= 0) 40 | { 41 | return new FormUrlDataConvertAttribute(); 42 | } 43 | else if (contentType.IndexOf("multipart/form-data", StringComparison.CurrentCultureIgnoreCase) >= 0) 44 | { 45 | return new MultiDataConvertAttribute(); 46 | } 47 | else 48 | { 49 | return new JsonDataConvertAttribute(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Data/IDataContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.Data 6 | { 7 | public interface IDataContext 8 | { 9 | 10 | string this[string name] { get; } 11 | 12 | void SetValue(string name, object value); 13 | 14 | bool TryGetBoolean(string name, out bool value); 15 | 16 | bool TryGetString(string name, out string value); 17 | 18 | bool TryGetDateTime(string name, out DateTime value); 19 | 20 | bool TryGetDecimal(string name, out decimal value); 21 | 22 | bool TryGetFloat(string name, out float value); 23 | 24 | bool TryGetDouble(string name, out double value); 25 | 26 | bool TryGetUShort(string name, out ushort value); 27 | 28 | bool TryGetUInt(string name, out uint value); 29 | 30 | bool TryGetULong(string name, out ulong value); 31 | 32 | bool TryGetInt(string name, out int value); 33 | 34 | bool TryGetLong(string name, out long value); 35 | 36 | bool TryGetShort(string name, out short value); 37 | 38 | object GetObject(string name, Type type); 39 | 40 | bool TryGetByte(string name, out byte value); 41 | 42 | bool TryGetChar(string name, out char value); 43 | 44 | IDictionary Copy(); 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/Data/InvalidDataFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.Data 6 | { 7 | public class InvalidDataFilter 8 | { 9 | private CharItem[] Items = new CharItem[256]; 10 | 11 | public void Add(params string[] items) 12 | { 13 | foreach (var item in items) 14 | { 15 | var bytes = Encoding.UTF8.GetByteCount(item); 16 | if (bytes == item.Length) 17 | { 18 | var kw = System.Web.HttpUtility.UrlEncode(item); 19 | OnAdd(item); 20 | OnAdd(kw); 21 | } 22 | } 23 | } 24 | 25 | private void OnAdd(string item) 26 | { 27 | byte index = (byte)char.ToUpper(item[0]); 28 | if (Items[index] == null) 29 | { 30 | Items[index] = new CharItem(item[0]); 31 | } 32 | Items[index].Add(item, 1); 33 | 34 | index = (byte)char.ToLower(item[0]); 35 | if (Items[index] == null) 36 | { 37 | Items[index] = new CharItem(item[0]); 38 | } 39 | Items[index].Add(item, 1); 40 | } 41 | 42 | public List Match(string value) 43 | { 44 | List result = new List(); 45 | int offset = 0; 46 | while (offset < value.Length) 47 | { 48 | var item = OnMatch(value, offset); 49 | if (item == null) 50 | { 51 | offset += 1; 52 | } 53 | else 54 | { 55 | offset += item.Data.Length; 56 | result.Add(item.Data); 57 | } 58 | } 59 | return result; 60 | } 61 | 62 | public int IsMatch(string value) 63 | { 64 | int count = 0; 65 | 66 | int offset = 0; 67 | while (offset < value.Length) 68 | { 69 | var item = OnMatch(value, offset); 70 | if (item == null) 71 | { 72 | offset += 1; 73 | } 74 | else 75 | { 76 | count++; 77 | offset += item.Data.Length; 78 | } 79 | } 80 | return count; 81 | } 82 | private CharItem OnMatch(string value, int offset) 83 | { 84 | byte index = (byte)value[offset]; 85 | var item = Items[index]; 86 | if (item == null) 87 | return null; 88 | else 89 | return item.Eof ? item : item.Match(value, offset + 1); 90 | } 91 | 92 | public class CharItem 93 | { 94 | public CharItem(char c) 95 | { 96 | Value = c; 97 | } 98 | 99 | public bool IsMatch(char value) 100 | { 101 | return Value == value || Value == char.ToLower(value) || Value == char.ToUpper(value); 102 | } 103 | 104 | 105 | public char Value { get; set; } 106 | 107 | public bool Eof { get; set; } = false; 108 | 109 | public string Data { get; set; } 110 | 111 | private CharItem[] Items = new CharItem[256]; 112 | 113 | public void Add(string key, int offset) 114 | { 115 | if (offset == key.Length) 116 | { 117 | Data = key; 118 | Eof = true; 119 | } 120 | else 121 | { 122 | byte index = (byte)char.ToUpper(key[offset]); 123 | if (Items[index] == null) 124 | { 125 | Items[index] = new CharItem(key[offset]); 126 | } 127 | Items[index].Add(key, offset + 1); 128 | 129 | index = (byte)char.ToLower(key[offset]); 130 | if (Items[index] == null) 131 | { 132 | Items[index] = new CharItem(key[offset]); 133 | } 134 | Items[index].Add(key, offset + 1); 135 | } 136 | } 137 | 138 | public CharItem Match(string value, int offset) 139 | { 140 | if (offset < value.Length) 141 | { 142 | byte index = (byte)value[offset]; 143 | var item = Items[index]; 144 | if (item != null) 145 | { 146 | if (item.IsMatch(value[offset])) 147 | { 148 | if (item.Eof) 149 | return item; 150 | else 151 | return item.Match(value, offset + 1); 152 | 153 | } 154 | } 155 | } 156 | return null; 157 | } 158 | } 159 | 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Data/NoDataConvertAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.Data 6 | { 7 | [AttributeUsage(AttributeTargets.Method)] 8 | public class NoDataConvertAttribute : Attribute 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/EventArgs.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using BeetleX.EventArgs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace BeetleX.FastHttpApi 8 | { 9 | public delegate void EventHttpServerLog(IServer server, BeetleX.EventArgs.ServerLogEventArgs e); 10 | 11 | public class ActionExecutedArgs 12 | { 13 | public string ServerType { get; set; } 14 | 15 | public string Url { get; set; } 16 | 17 | public string HTTPMethod { get; set; } 18 | 19 | public ActionHandler ActionHandler { get; set; } 20 | 21 | public IDictionary Headers { get; set; } 22 | 23 | public long UseTime { get; set; } 24 | 25 | public DateTime Time { get; set; } 26 | 27 | public int Code { get; set; } = 200; 28 | 29 | public Exception Exception { get; set; } 30 | 31 | } 32 | 33 | public class HttpServerLogEventArgs : BeetleX.EventArgs.ServerLogEventArgs 34 | { 35 | public HttpServerLogEventArgs(object tag, string message, LogType type, ISession session = null) 36 | : base(message, type, session) 37 | { 38 | Tag = tag; 39 | } 40 | public object Tag { get; private set; } 41 | 42 | public bool OutputConsole { get; set; } = true; 43 | 44 | public bool OutputFile { get; set; } = true; 45 | } 46 | 47 | 48 | public struct EventHttpResponsedArgs 49 | { 50 | public EventHttpResponsedArgs(HttpRequest request, HttpResponse response, double time, int status, string statusMsg) 51 | { 52 | Request = request; 53 | Response = response; 54 | Time = time; 55 | Status = status; 56 | StatusMessage = statusMsg; 57 | } 58 | 59 | public HttpRequest Request { get; set; } 60 | 61 | public HttpResponse Response { get; set; } 62 | 63 | public double Time 64 | { 65 | get; set; 66 | } 67 | 68 | public int Status { get; set; } 69 | 70 | public string StatusMessage { get; set; } 71 | } 72 | 73 | public class WebSocketConnectArgs : System.EventArgs 74 | { 75 | public WebSocketConnectArgs(HttpRequest request, HttpResponse response) 76 | { 77 | Request = request; 78 | Response = response; 79 | Cancel = false; 80 | 81 | } 82 | 83 | public UpgradeWebsocketError Error { get; set; } 84 | 85 | public IResult UpgradeSuccess { get; set; } 86 | 87 | public HttpResponse Response { get; internal set; } 88 | 89 | public HttpRequest Request { get; internal set; } 90 | 91 | public bool Cancel { get; set; } 92 | } 93 | 94 | public class EventControllerInstanceArgs : System.EventArgs 95 | { 96 | public Type Type { get; internal set; } 97 | 98 | public object Controller { get; set; } 99 | 100 | public IHttpContext Context { get; internal set; } 101 | 102 | 103 | } 104 | 105 | public class EventParameterBinding : System.EventArgs 106 | { 107 | public Type Type { get; internal set; } 108 | 109 | public object Parameter { get; set; } 110 | 111 | public IHttpContext Context { get; internal set; } 112 | 113 | public ActionHandler ActionHandler { get; internal set; } 114 | } 115 | 116 | public class EventActionExecutingArgs : System.EventArgs 117 | { 118 | 119 | 120 | public ActionHandler Handler { get; internal set; } 121 | 122 | public IHttpContext HttpContext { get; internal set; } 123 | 124 | public bool Cancel { get; set; } 125 | } 126 | 127 | 128 | public class EventHttpRequestArgs : System.EventArgs 129 | { 130 | public HttpRequest Request { get; internal set; } 131 | 132 | public HttpResponse Response { get; internal set; } 133 | 134 | public bool Cancel { get; set; } 135 | } 136 | 137 | public class EventHttpInnerErrorArgs : EventHttpRequestArgs 138 | { 139 | public string Code { get; internal set; } 140 | 141 | public string Message { get; internal set; } 142 | 143 | public Exception Error { get; internal set; } 144 | 145 | } 146 | 147 | public class EventHttpServerStartedArgs : System.EventArgs 148 | { 149 | public HttpApiServer HttpApiServer { get; internal set; } 150 | } 151 | 152 | public class EventOptionsReloadArgs : System.EventArgs 153 | { 154 | public HttpApiServer HttpApiServer { get; internal set; } 155 | 156 | public HttpOptions HttpOptions { get; internal set; } 157 | } 158 | 159 | public class EventActionRegistingArgs 160 | { 161 | public bool Cancel { get; set; } = false; 162 | 163 | public HttpApiServer Server { get; internal set; } 164 | 165 | public ActionHandler Handler { get; internal set; } 166 | 167 | public string Url { get; set; } 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/FileLog.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Dispatchs; 2 | using BeetleX.EventArgs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading; 7 | 8 | namespace BeetleX.FastHttpApi 9 | { 10 | 11 | public class FileLogWriter 12 | { 13 | public FileLogWriter(string type) 14 | { 15 | Type = type; 16 | mLogPath = System.IO.Directory.GetCurrentDirectory() + 17 | System.IO.Path.DirectorySeparatorChar + "logs" + System.IO.Path.DirectorySeparatorChar; 18 | if (!System.IO.Directory.Exists(mLogPath)) 19 | { 20 | System.IO.Directory.CreateDirectory(mLogPath); 21 | } 22 | mDispatcher = new SingleThreadDispatcher(OnWriteLog); 23 | } 24 | 25 | public string Type { get; private set; } 26 | 27 | private string mLogPath; 28 | 29 | private int FileIndex = 0; 30 | 31 | private SingleThreadDispatcher mDispatcher; 32 | 33 | private System.IO.StreamWriter mWriter; 34 | 35 | private int mWriteCount; 36 | 37 | protected System.IO.StreamWriter GetWriter() 38 | { 39 | if (mWriter == null || mWriter.BaseStream.Length > 1024 * 1024 * 20) 40 | { 41 | if (mWriter != null) 42 | { 43 | mWriter.Flush(); 44 | mWriter.Close(); 45 | } 46 | string filename; 47 | do 48 | { 49 | filename = mLogPath + Type + "_" + DateTime.Now.ToString("yyyyMMdd") + "_" + ++FileIndex + ".txt"; 50 | } while (System.IO.File.Exists(filename)); 51 | mWriter = new System.IO.StreamWriter(filename, false, Encoding.UTF8); 52 | 53 | } 54 | return mWriter; 55 | 56 | } 57 | 58 | private void OnWriteLog(LogItem e) 59 | { 60 | mWriteCount++; 61 | System.IO.StreamWriter writer = GetWriter(); 62 | writer.Write(DateTime.Now); 63 | writer.Write("\t"); 64 | writer.Write(e.Type.ToString()); 65 | writer.Write("\t"); 66 | writer.Write(e.RemoveIP!=null? e.RemoveIP:"SYSTEM"); 67 | writer.Write("\t"); 68 | writer.WriteLine(e.Message); 69 | if (mWriteCount > 200 || mDispatcher.Count == 0) 70 | { 71 | writer.Flush(); 72 | mWriteCount = 0; 73 | } 74 | } 75 | 76 | public void Add(string removeIP,LogType type, string message) 77 | { 78 | Add(new LogItem(removeIP, type, message)); 79 | } 80 | 81 | public void Add(LogItem e) 82 | { 83 | mDispatcher.Enqueue(e); 84 | } 85 | 86 | public class LogItem 87 | { 88 | public LogItem(string removeIP, LogType type, string message) 89 | { 90 | RemoveIP = removeIP; 91 | Type = type; 92 | Message = message; 93 | } 94 | public string RemoveIP; 95 | public LogType Type; 96 | public string Message; 97 | } 98 | 99 | public void Run() 100 | { 101 | 102 | } 103 | } 104 | 105 | public class LogRecord 106 | { 107 | public string Type { get; set; } 108 | 109 | public string Time { get; set; } 110 | 111 | public string Message { get; set; } 112 | public string RemoveIP { get; set; } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/HeaderStringHelper.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | using System.Text; 6 | 7 | namespace BeetleX.FastHttpApi 8 | { 9 | public class HeaderStringHelper 10 | { 11 | public const int CACHE_STRING_MAXLENGTH = 512; 12 | 13 | public const int CACHE_MAXSIZE = 1024 * 32; 14 | 15 | public static int Count => mStringCache.Count; 16 | 17 | private static Dictionary mStringCache = new Dictionary(); 18 | 19 | public unsafe static string GetString(ReadOnlySpan data, bool toLower = false) 20 | { 21 | int length = data.Length; 22 | if (toLower) 23 | { 24 | fixed (char* ptr = data) 25 | { 26 | char* each = ptr; 27 | for (int i = 0; i < length; i++) 28 | { 29 | *each = char.ToLower(*each); 30 | each++; 31 | } 32 | } 33 | } 34 | ulong code = GetHashCode(data); 35 | if (!mStringCache.TryGetValue(code, out string result)) 36 | { 37 | result = new string(data); 38 | lock (mStringCache) 39 | { 40 | if (Count < CACHE_MAXSIZE) 41 | { 42 | mStringCache[code] = result; 43 | } 44 | } 45 | } 46 | return result; 47 | 48 | } 49 | 50 | public static unsafe ulong GetHashCode(ReadOnlySpan data) 51 | { 52 | int length = data.Length; 53 | fixed (char* ptr = data) 54 | { 55 | int l = 0; 56 | int num = 352654597; 57 | int num2 = num; 58 | int* ptr2 = (int*)ptr; 59 | int i = length % 2; 60 | int count = length / 2; 61 | for (l = 0; l < count; l++) 62 | { 63 | if (l % 2 == 0) 64 | { 65 | num = ((num << 5) + num + (num >> 27) ^ *ptr2); 66 | } 67 | else 68 | { 69 | num2 = ((num2 << 5) + num2 + (num2 >> 27) ^ ptr2[1]); 70 | } 71 | ptr2 += 1; 72 | } 73 | if (i > 0) 74 | { 75 | num = ((num << 5) + num + (num >> 27) ^ (int)data[length - 1]); 76 | } 77 | uint tag = (uint)data[0] << 8 | (uint)data[length - 1]; 78 | var result = num + num2 * 1566083941; 79 | return (ulong)result << 28 | (ulong)length << 16 | tag; 80 | } 81 | } 82 | } 83 | 84 | class StringBuffer 85 | { 86 | 87 | public StringBuffer() 88 | { 89 | Bytes = new byte[1024 * 4]; 90 | Chars = new Char[1024 * 4]; 91 | } 92 | 93 | public byte[] Bytes; 94 | 95 | public Char[] Chars; 96 | } 97 | 98 | public static class PipeStreamExtend 99 | { 100 | [ThreadStatic] 101 | private static StringBuffer stringBuffer; 102 | 103 | public static bool ReadLine(this PipeStream stream, out Span line) 104 | { 105 | //line = default; 106 | //if (stringBuffer == null) 107 | // stringBuffer = new StringBuffer(); 108 | //int count = stream.IndexOf(HeaderTypeFactory.LINE_BYTES, stringBuffer.Bytes); 109 | //if (count > 0) 110 | //{ 111 | // stream.ReadFree(count); 112 | // var len = Encoding.ASCII.GetChars(stringBuffer.Bytes, 0, count - 2, stringBuffer.Chars, 0); 113 | // line = new Span(stringBuffer.Chars, 0, len); 114 | // return true; 115 | //} 116 | //return false; 117 | line = default; 118 | if (stringBuffer == null) 119 | stringBuffer = new StringBuffer(); 120 | var indexof = stream.IndexOf(HeaderTypeFactory.LINE_BYTES); 121 | if (indexof.End != null) 122 | { 123 | stream.Read(stringBuffer.Bytes, 0, indexof.Length); 124 | var len = Encoding.ASCII.GetChars(stringBuffer.Bytes, 0, indexof.Length - 2, stringBuffer.Chars, 0); 125 | line = new Span(stringBuffer.Chars, 0, len); 126 | return true; 127 | } 128 | return false; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/HttpApiServer_map.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | public partial class HttpApiServer 9 | { 10 | private Dictionary> mMapUrlAction = new Dictionary>(StringComparer.OrdinalIgnoreCase); 11 | 12 | private Dictionary>> mMapUrlTaskAction = new Dictionary>>(StringComparer.OrdinalIgnoreCase); 13 | 14 | private string OnGetMapURL(string route) 15 | { 16 | var ra = new RouteTemplateAttribute(route); 17 | if (route[0] != '/') 18 | route = '/' + route; 19 | var reurl = ra.Analysis(null); 20 | string url = route; 21 | if (!string.IsNullOrEmpty(reurl)) 22 | { 23 | url = route.Substring(0, route.IndexOf("{")); 24 | if (url[url.Length - 1] == '/') 25 | { 26 | url = url.Substring(0, url.Length - 1); 27 | } 28 | UrlRewrite.Add(null, reurl, url); 29 | } 30 | return url; 31 | } 32 | 33 | private bool OnExecuteMap(HttpRequest request) 34 | { 35 | if (mMapUrlAction.Count == 0 && mMapUrlTaskAction.Count == 0) 36 | return false; 37 | HttpContext context = new HttpContext(this, request, request.Response, request.Data); 38 | if (mMapUrlAction.TryGetValue(request.BaseUrl, out Func hanlder)) 39 | { 40 | OnExecuteMapAction(hanlder, context); 41 | return true; 42 | } 43 | if (mMapUrlTaskAction.TryGetValue(request.BaseUrl, out Func> action)) 44 | { 45 | OnExecuteMapFun(action, context); 46 | return true; 47 | } 48 | return false; 49 | } 50 | 51 | private void OnExecuteMapAction(Func action, IHttpContext context) 52 | { 53 | try 54 | { 55 | var result = action(context); 56 | context.Result(result); 57 | } 58 | catch (Exception e_) 59 | { 60 | //context.Result(new InnerErrorResult("500", e_, this.Options.OutputStackTrace)); 61 | context.Response.InnerError("500","execute map action error!", e_, this.Options.OutputStackTrace); 62 | GetLog(EventArgs.LogType.Error)?.Log(EventArgs.LogType.Error, context.Session, $"HTTP Map {context.Request.BaseUrl} execute error {e_.Message} {e_.StackTrace}"); 63 | } 64 | } 65 | 66 | private async void OnExecuteMapFun(Func> action, IHttpContext context) 67 | { 68 | try 69 | { 70 | var result = await action(context); 71 | context.Result(result); 72 | } 73 | catch (Exception e_) 74 | { 75 | //context.Result(new InnerErrorResult("500", e_, this.Options.OutputStackTrace)); 76 | context.Response.InnerError("500", "execute map action error!", e_, this.Options.OutputStackTrace); 77 | GetLog(EventArgs.LogType.Error)?.Log(EventArgs.LogType.Error, context.Session, $"HTTP Map {context.Request.BaseUrl} execute error {e_.Message} {e_.StackTrace}"); 78 | } 79 | } 80 | 81 | public HttpApiServer Map(string url, Func action) 82 | { 83 | var mapurl = OnGetMapURL(url); 84 | mMapUrlAction[mapurl] = action; 85 | return this; 86 | } 87 | 88 | public HttpApiServer Map(string url, Func> action) 89 | { 90 | var mapurl = OnGetMapURL(url); 91 | mMapUrlTaskAction[mapurl] = action; 92 | return this; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/HttpConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "HttpConfig": { 3 | "Host": "", 4 | "Port": 9090, 5 | "SSL": false, 6 | "CertificateFile": "", 7 | "CertificatePassword": "", 8 | "MaxBodyLength": 2097152, 9 | "OutputStackTrace": false, 10 | "StaticResurceType": "xml;svg;woff;woff2;jpg;jpeg;gif;png;js;html;htm;css;txt;ico;zip;rar", 11 | "DefaultPage": "index.html;index.htm", 12 | "NotLoadFolder": "\\Files;\\Images;\\Data", 13 | "Manager": "admin", 14 | "ManagerPWD": "123456", 15 | "NoGzipFiles": "jpg;jpeg;png;gif;png;ico;zip;rar;bmp", 16 | "CacheFiles": "html;htm;js;css", 17 | "BufferSize": 1024, 18 | "WebSocketMaxRPS": 1000, 19 | "WriteLog": true, 20 | "LogToConsole": true, 21 | "LogLevel": "Warring", 22 | "FileManager": false 23 | } 24 | } -------------------------------------------------------------------------------- /src/HttpToken.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using BeetleX.EventArgs; 3 | using BeetleX.FastHttpApi.WebSockets; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Text; 9 | 10 | namespace BeetleX.FastHttpApi 11 | { 12 | public class HttpToken 13 | { 14 | public HttpToken() 15 | { 16 | FirstRequest = true; 17 | CreateTime = DateTime.Now; 18 | RequestArgs = new EventHttpRequestArgs(); 19 | } 20 | 21 | internal BeetleX.Dispatchs.SingleThreadDispatcher IOQueue { get; set; } 22 | 23 | internal EventHttpRequestArgs RequestArgs { get; set; } 24 | 25 | public bool KeepAlive { get; internal set; } 26 | 27 | internal StaticResurce.FileBlock File { get; set; } 28 | 29 | public bool WebSocket { get; internal set; } 30 | 31 | public HttpRequest Request { get; internal set; } 32 | 33 | internal bool FirstRequest { get; set; } 34 | 35 | public DateTime CreateTime { get; internal set; } 36 | 37 | public PipeStream WebSocketData { get; set; } 38 | 39 | public Newtonsoft.Json.JsonSerializer WebSocketJsonSerializer { get; set; } 40 | 41 | internal RpsLimit WSRpsLimit { get; set; } 42 | 43 | internal RpsLimit HttpRpsLimit { get; set; } 44 | 45 | internal DataPacketType WSLastPacketType { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/IActionContextParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public interface IActionParameter : IDisposable 8 | { 9 | ActionContext Context { get; set; } 10 | void Init(IHttpContext context); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/IActionResultHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | public interface IActionResultHandler 9 | { 10 | void Success(object result); 11 | void Error(Exception e_, EventArgs.LogType logType = EventArgs.LogType.Error, int code = 500); 12 | 13 | } 14 | 15 | 16 | class WSActionResultHandler : IActionResultHandler 17 | { 18 | public WSActionResultHandler(WebsocketJsonContext jsonContext, HttpApiServer server, HttpRequest request, 19 | ActionResult result, WebSockets.DataFrame dataFrame, long startTime) 20 | { 21 | DataContext = jsonContext; 22 | Server = server; 23 | Request = request; 24 | Result = result; 25 | DataFrame = dataFrame; 26 | StartTime = startTime; 27 | } 28 | 29 | public ActionHandler ActionHandler { get; set; } 30 | 31 | public WebsocketJsonContext DataContext; 32 | 33 | public HttpApiServer Server; 34 | 35 | public HttpRequest Request; 36 | 37 | public ActionResult Result; 38 | 39 | public WebSockets.DataFrame DataFrame; 40 | 41 | public void Error(Exception e_, EventArgs.LogType logType = EventArgs.LogType.Error, int code = 500) 42 | { 43 | 44 | Server.OnActionExecutedError(DataContext, ActionHandler, e_, code, StartTime); 45 | if (Server.EnableLog(logType)) 46 | Server.Log(logType, Request.Session, $"Websocket {Request.ID} {Request.RemoteIPAddress} execute {DataContext.ActionUrl} inner error {e_.Message}@{e_.StackTrace}"); 47 | Result.Code = code; 48 | Result.Error = e_.Message; 49 | if (Server.Options.OutputStackTrace) 50 | { 51 | Result.StackTrace = e_.StackTrace; 52 | } 53 | DataFrame.Send(Request.Session, true); 54 | } 55 | 56 | public long StartTime; 57 | 58 | public void Success(object result) 59 | { 60 | Server.OnActionExecutedSuccess(DataContext, ActionHandler, StartTime); 61 | var data = Request.Server.WSActionResultHandler?.Invoke(Request, ActionHandler, result); 62 | if (data != null) 63 | { 64 | DataFrame.Body = data; 65 | } 66 | else 67 | { 68 | if (result is ActionResult) 69 | { 70 | Result = (ActionResult)result; 71 | Result.ID = DataContext.RequestID; 72 | if (Result.Url == null) 73 | Result.Url = DataContext.ActionUrl; 74 | DataFrame.Body = Result; 75 | } 76 | else 77 | { 78 | Result.Data = result; 79 | } 80 | } 81 | DataFrame.Send(Request.Session, false); 82 | if (Server.EnableLog(EventArgs.LogType.Info)) 83 | Server.Log(EventArgs.LogType.Info, Request.Session, $"Websocket {Request.ID} {Request.RemoteIPAddress} execute {DataContext.ActionUrl} action use time:{ Server.BaseServer.GetRunTime() - StartTime}ms"); 84 | } 85 | 86 | 87 | } 88 | 89 | class HttpActionResultHandler : IActionResultHandler 90 | { 91 | 92 | public HttpActionResultHandler(HttpContext context, HttpApiServer server, HttpRequest request, HttpResponse response, long startTime) 93 | { 94 | Server = server; 95 | Request = request; 96 | Response = response; 97 | StartTime = startTime; 98 | Context = context; 99 | } 100 | 101 | public HttpContext Context; 102 | 103 | public HttpApiServer Server; 104 | 105 | public HttpRequest Request; 106 | 107 | public HttpResponse Response; 108 | 109 | public long StartTime; 110 | 111 | public void Error(Exception e_, EventArgs.LogType logType = EventArgs.LogType.Error, int code = 500) 112 | { 113 | Server.OnActionExecutedError(Context, Request.ActionHandler, e_, code, StartTime); 114 | if (Server.EnableLog(logType)) 115 | Server.Log(logType, Request.Session, 116 | $"HTTP {Request.ID} {Request.RemoteIPAddress} {Request.Method} { Request.Url} inner error {e_.Message}@{e_.StackTrace}"); 117 | Response.InnerError(code.ToString(), $"http execute {Request.BaseUrl} inner error!", e_, Server.Options.OutputStackTrace); 118 | } 119 | 120 | public void Success(object result) 121 | { 122 | Server.OnActionExecutedSuccess(Context, Request.ActionHandler, StartTime); 123 | if (Server.EnableLog(EventArgs.LogType.Info)) 124 | Server.BaseServer.Log(EventArgs.LogType.Info, Request.Session, 125 | $"HTTP {Request.ID} {Request.RemoteIPAddress} {Request.Method} {Request.BaseUrl} use time:{Server.BaseServer.GetRunTime() - StartTime}ms"); 126 | if (result is FileResult fileResult) 127 | { 128 | Server.ResourceCenter.OutputFile(fileResult, Request, Response); 129 | } 130 | else 131 | { 132 | Response.Result(result); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/IDGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | 7 | namespace BeetleX.FastHttpApi 8 | { 9 | //1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 10 | //[组 256 ] [2010-1-1到当前时间秒 max:68719476735] [自增值 最大:1000000] 11 | // [可用年数:2179] 12 | public class IDGenerator 13 | { 14 | 15 | private byte mGroup = 1; 16 | 17 | private long mSeconds; 18 | 19 | private ulong mID = 1; 20 | 21 | private long mLastTime; 22 | 23 | private System.Diagnostics.Stopwatch mWatch = new System.Diagnostics.Stopwatch(); 24 | 25 | public IDGenerator() 26 | { 27 | LoadIPAddress(); 28 | Init(); 29 | } 30 | 31 | public static IDGenerator Default { get; set; } = new IDGenerator(); 32 | 33 | public IDGenerator(byte group) 34 | { 35 | mGroup = group; 36 | Init(); 37 | } 38 | private void LoadIPAddress() 39 | { 40 | var host = Dns.GetHostEntry(Dns.GetHostName()); 41 | foreach (var ip in host.AddressList) 42 | { 43 | if (ip.AddressFamily == AddressFamily.InterNetwork) 44 | { 45 | var value = ip.GetAddressBytes(); 46 | if (value[0] == 10) 47 | { 48 | mGroup = value[3]; 49 | break; 50 | 51 | } 52 | if (value[0] == 172) 53 | { 54 | if (value[1] >= 16 && value[1] <= 31) 55 | { 56 | mGroup = value[3]; 57 | break; 58 | } 59 | } 60 | if (value[0] == 192 && value[1] == 168) 61 | { 62 | mGroup = value[3]; 63 | break; 64 | } 65 | } 66 | } 67 | if (mGroup == 0) 68 | { 69 | mGroup = 1; 70 | } 71 | } 72 | 73 | private void Init() 74 | { 75 | var ts = DateTime.Now - DateTime.Parse("2010-1-1"); 76 | mSeconds = (long)ts.TotalSeconds; 77 | mWatch.Restart(); 78 | mLastTime = (long)Math.Floor(mWatch.Elapsed.TotalSeconds); 79 | } 80 | 81 | public ulong Next() 82 | { 83 | lock (this) 84 | { 85 | ulong result = 0; 86 | result |= (ulong)mGroup << 56; 87 | mID++; 88 | START: 89 | var now = (long)Math.Floor(mWatch.Elapsed.TotalSeconds); 90 | if (now - mLastTime > 1) 91 | { 92 | mID = 1; 93 | mLastTime = now; 94 | } 95 | if (mID > 1000000) 96 | { 97 | System.Threading.Thread.Sleep(50); 98 | goto START; 99 | } 100 | result |= (ulong)(mSeconds + mLastTime) << 20; 101 | result |= mID; 102 | return result; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/IDataContext.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.FastHttpApi.Data; 2 | using BeetleX.FastHttpApi.WebSockets; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace BeetleX.FastHttpApi 9 | { 10 | 11 | 12 | class WebsocketJsonContext : IHttpContext 13 | { 14 | public WebsocketJsonContext(HttpApiServer server, HttpRequest request, IDataContext dataContext) 15 | { 16 | Server = server; 17 | Request = request; 18 | AsyncResult = false; 19 | mDataContext = dataContext; 20 | } 21 | 22 | private Data.IDataContext mDataContext; 23 | 24 | public HttpRequest Request { get; set; } 25 | 26 | public HttpResponse Response { get; set; } 27 | 28 | public HttpApiServer Server { get; set; } 29 | 30 | public object Tag { get; set; } 31 | 32 | public NextQueue Queue { get; set; } 33 | 34 | public string RequestID { get; set; } 35 | 36 | public void Result(object data) 37 | { 38 | WebSockets.DataFrame frame = data as WebSockets.DataFrame; 39 | if (frame == null) 40 | { 41 | ActionResult result = data as ActionResult; 42 | if (result == null) 43 | { 44 | result = new ActionResult(); 45 | result.Data = data; 46 | } 47 | result.ID = RequestID; 48 | if (result.Url == null) 49 | result.Url = this.ActionUrl; 50 | frame = Server.CreateDataFrame(result); 51 | } 52 | frame.Send(Request.Session); 53 | } 54 | 55 | internal bool AsyncResult { get; set; } 56 | 57 | public bool WebSocket => true; 58 | 59 | public IDataContext Data => mDataContext; 60 | 61 | public ISession Session => Request.Session; 62 | 63 | public string ActionUrl { get; internal set; } 64 | 65 | public object this[string name] { get => Session[name]; set => Session[name] = value; } 66 | 67 | public void Async() 68 | { 69 | AsyncResult = true; 70 | } 71 | 72 | 73 | public void SendToWebSocket(WebSockets.DataFrame data, HttpRequest request) 74 | { 75 | Server.SendToWebSocket(data, request); 76 | } 77 | 78 | public void SendToWebSocket(WebSockets.DataFrame data, Func filter = null) 79 | { 80 | Server.SendToWebSocket(data, filter); 81 | } 82 | 83 | 84 | public void SendToWebSocket(ActionResult data, HttpRequest request) 85 | { 86 | 87 | if (data.Url == null) 88 | data.Url = this.ActionUrl; 89 | DataFrame frame = Server.CreateDataFrame(data); 90 | Server.SendToWebSocket(frame, request); 91 | 92 | } 93 | 94 | public void SendToWebSocket(ActionResult data, Func filter = null) 95 | { 96 | if (data.Url == null) 97 | data.Url = this.ActionUrl; 98 | DataFrame frame = Server.CreateDataFrame(data); 99 | Server.SendToWebSocket(frame, filter); 100 | } 101 | 102 | 103 | } 104 | 105 | class HttpContext : IHttpContext 106 | { 107 | 108 | public HttpContext(HttpApiServer server, HttpRequest request, HttpResponse response, IDataContext dataContext) 109 | { 110 | Request = request; 111 | Response = response; 112 | Server = server; 113 | mDataContext = dataContext; 114 | } 115 | 116 | private Data.IDataContext mDataContext; 117 | 118 | public NextQueue Queue { get; set; } 119 | 120 | public HttpRequest Request { get; set; } 121 | 122 | public HttpResponse Response { get; set; } 123 | 124 | public HttpApiServer Server { get; set; } 125 | 126 | public object Tag { get; set; } 127 | 128 | public bool WebSocket => false; 129 | 130 | public IDataContext Data => mDataContext; 131 | 132 | public ISession Session => Request.Session; 133 | 134 | public string ActionUrl { get; internal set; } 135 | 136 | public object this[string name] { get => Session[name]; set => Session[name] = value; } 137 | 138 | public void Result(object data) 139 | { 140 | Response.Result(data); 141 | } 142 | 143 | 144 | public void SendToWebSocket(WebSockets.DataFrame data, HttpRequest request) 145 | { 146 | Server.SendToWebSocket(data, request); 147 | } 148 | 149 | public void SendToWebSocket(WebSockets.DataFrame data, Func filter = null) 150 | { 151 | Server.SendToWebSocket(data, filter); 152 | } 153 | 154 | 155 | public void SendToWebSocket(ActionResult data, HttpRequest request) 156 | { 157 | 158 | if (data.Url == null) 159 | data.Url = this.ActionUrl; 160 | DataFrame frame = Server.CreateDataFrame(data); 161 | Server.SendToWebSocket(frame, request); 162 | 163 | } 164 | 165 | public void SendToWebSocket(ActionResult data, Func filter = null) 166 | { 167 | if (data.Url == null) 168 | data.Url = this.ActionUrl; 169 | DataFrame frame = Server.CreateDataFrame(data); 170 | Server.SendToWebSocket(frame, filter); 171 | } 172 | 173 | 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/IDataResponse.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | public interface IDataResponse 9 | { 10 | void Write(PipeStream stream); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/IHttpContext.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.FastHttpApi.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | public interface IHttpContext 9 | { 10 | HttpRequest Request { get; } 11 | 12 | HttpResponse Response { get; } 13 | 14 | HttpApiServer Server { get; } 15 | 16 | ISession Session { get; } 17 | 18 | object this[string name] { get; set; } 19 | 20 | object Tag { get; } 21 | 22 | void Result(object data); 23 | 24 | void SendToWebSocket(ActionResult data, HttpRequest request); 25 | 26 | void SendToWebSocket(ActionResult data, Func filter = null); 27 | 28 | void SendToWebSocket(WebSockets.DataFrame data, HttpRequest request); 29 | 30 | void SendToWebSocket(WebSockets.DataFrame data, Func filter = null); 31 | 32 | // void Async(); 33 | 34 | bool WebSocket { get; } 35 | 36 | IDataContext Data { get; } 37 | 38 | string ActionUrl { get; } 39 | 40 | NextQueue Queue { get; set; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/IRpsLimitHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public interface IRpsLimitHandler 8 | { 9 | string ID { get; set; } 10 | 11 | string Name { get; set; } 12 | 13 | bool Check(HttpRequest request, HttpResponse response); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/LRUCached.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Collections.Concurrent; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | public class LRUCached 9 | { 10 | public LRUCached(StringComparer stringComparer) 11 | { 12 | if (stringComparer == null) 13 | stringComparer = StringComparer.Ordinal; 14 | mKeyCached = new ConcurrentDictionary>(stringComparer); 15 | } 16 | 17 | public int MaxSize { get; set; } = 200000; 18 | 19 | public Action Removed { get; set; } 20 | 21 | private ConcurrentDictionary> mKeyCached; 22 | 23 | private LinkedList mCachedItems = new LinkedList(); 24 | 25 | private void ActiveItem(LinkedListNode item) 26 | { 27 | lock (mCachedItems) 28 | { 29 | if (item.List == mCachedItems) 30 | { 31 | if (item != mCachedItems.First) 32 | { 33 | mCachedItems.Remove(item); 34 | mCachedItems.AddFirst(item); 35 | } 36 | } 37 | } 38 | } 39 | 40 | private void AddItem(LinkedListNode item) 41 | { 42 | string removeKey = null; 43 | lock (mCachedItems) 44 | { 45 | if (mCachedItems.Count >= MaxSize) 46 | { 47 | removeKey = mCachedItems.Last.Value.Key; 48 | mCachedItems.RemoveLast(); 49 | } 50 | mCachedItems.AddFirst(item); 51 | } 52 | if (removeKey != null) 53 | if (mKeyCached.TryRemove(removeKey, out LinkedListNode del)) 54 | { 55 | Removed?.Invoke(del.Value.Value); 56 | } 57 | } 58 | 59 | private void RemoveItem(LinkedListNode item) 60 | { 61 | lock (mCachedItems) 62 | { 63 | if (item.List == mCachedItems) 64 | mCachedItems.Remove(item); 65 | } 66 | } 67 | 68 | public void Remove(string key) 69 | { 70 | if (mKeyCached.TryRemove(key, out LinkedListNode del)) 71 | { 72 | RemoveItem(del); 73 | } 74 | } 75 | 76 | public object ExistOrAdd(string key, object item) 77 | { 78 | LinkedListNode node = new LinkedListNode(new CacheItem(key, item)); 79 | if (mKeyCached.TryAdd(key, node)) 80 | { 81 | AddItem(node); 82 | return null; 83 | } 84 | if (mKeyCached.TryGetValue(key, out node)) 85 | ActiveItem(node); 86 | return node?.Value.Value; 87 | } 88 | 89 | public bool TryGetValue(string key, out T value) 90 | { 91 | var obj = Get(key); 92 | value = (T)obj; 93 | return obj != null; 94 | } 95 | 96 | public object Get(string key) 97 | { 98 | if (mKeyCached.TryGetValue(key, out LinkedListNode item)) 99 | { 100 | ActiveItem(item); 101 | return item.Value.Value; 102 | } 103 | return null; 104 | } 105 | 106 | class CacheItem 107 | { 108 | 109 | public CacheItem(string key, object value) 110 | { 111 | Key = key; 112 | Value = value; 113 | } 114 | public string Key { get; set; } 115 | 116 | public object Value { get; set; } 117 | } 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/NotLoadResourceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] 8 | public class NotLoadResourceAttribute : Attribute 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | 8 | class ObjectPoolGroup 9 | { 10 | private List> objectPools = new List>(); 11 | 12 | private long mIndex = 0; 13 | 14 | public ObjectPoolGroup(int maxItem = 5000) 15 | { 16 | for (int i = 0; i < Math.Min(Environment.ProcessorCount, 16); i++) 17 | { 18 | objectPools.Add(new ObjectPool(maxItem)); 19 | } 20 | } 21 | public bool Push(T data) 22 | { 23 | return objectPools[Math.Abs(data.GetHashCode()) % objectPools.Count].Push(data); 24 | } 25 | public bool TryPop(out T data) 26 | { 27 | return objectPools[(int)(++mIndex % objectPools.Count)].TryPop(out data); 28 | } 29 | 30 | } 31 | 32 | class ObjectPool 33 | { 34 | 35 | public ObjectPool(int maxItems = 5000) 36 | { 37 | mMaxItems = maxItems; 38 | 39 | } 40 | 41 | private int mMaxItems; 42 | 43 | private System.Collections.Concurrent.ConcurrentStack mQueues = new System.Collections.Concurrent.ConcurrentStack(); 44 | 45 | private int mCount; 46 | 47 | public bool Push(T data) 48 | { 49 | int value = System.Threading.Interlocked.Increment(ref mCount); 50 | if (value < mMaxItems) 51 | { 52 | mQueues.Push(data); 53 | return true; 54 | } 55 | else 56 | { 57 | System.Threading.Interlocked.Decrement(ref mCount); 58 | return false; 59 | } 60 | } 61 | 62 | public bool TryPop(out T data) 63 | { 64 | bool result; 65 | result = mQueues.TryPop(out data); 66 | if (result) 67 | System.Threading.Interlocked.Decrement(ref mCount); 68 | return result; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/OptionsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using BeetleX.Buffers; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] 9 | public class OptionsAttribute : Attribute, IResult 10 | { 11 | public OptionsAttribute() 12 | { 13 | HasBody = false; 14 | } 15 | 16 | public virtual IHeaderItem ContentType => ContentTypes.TEXT_UTF8; 17 | 18 | public int Length { get; set; } 19 | 20 | public virtual bool HasBody { get; set; } 21 | 22 | public string AllowOrigin { get; set; } 23 | 24 | public string AllowMethods { get; set; } 25 | 26 | public string AllowHeaders { get; set; } 27 | 28 | public string AllowMaxAge { get; set; } 29 | 30 | public string AllowCredentials { get; set; } 31 | 32 | public string Vary { get; set; } = "Origin"; 33 | 34 | public virtual void Setting(HttpResponse response) 35 | { 36 | if (!string.IsNullOrEmpty(AllowOrigin)) 37 | { 38 | response.Header["Access-Control-Allow-Origin"] = AllowOrigin; 39 | if (AllowOrigin != "*") 40 | response.Header["Vary"] = Vary; 41 | } 42 | if (!string.IsNullOrEmpty(AllowMethods)) 43 | { 44 | response.Header["Access-Control-Allow-Methods"] = AllowMethods; 45 | } 46 | 47 | if (!string.IsNullOrEmpty(AllowHeaders)) 48 | { 49 | response.Header["Access-Control-Allow-Headers"] = AllowHeaders; 50 | } 51 | 52 | if (!string.IsNullOrEmpty(AllowMaxAge)) 53 | { 54 | response.Header["Access-Control-Max-Age"] = AllowMaxAge; 55 | } 56 | if (!string.IsNullOrEmpty(AllowCredentials)) 57 | { 58 | response.Header["Access-Control-Allow-Credentials"] = AllowCredentials; 59 | } 60 | } 61 | 62 | public virtual void Write(PipeStream stream, HttpResponse response) 63 | { 64 | 65 | } 66 | 67 | public virtual void SetResponse(HttpRequest request, HttpResponse response) 68 | { 69 | HttpApiServer server = request.Server; 70 | if (server.EnableLog(EventArgs.LogType.Debug)) 71 | server.Log(EventArgs.LogType.Debug, request.Session, $"{request.RemoteIPAddress} {request.Method} {request.Url} set options"); 72 | response.Header["Access-Control-Allow-Origin"] = AllowOrigin; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/PostAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Method)] 8 | public class PostAttribute : Attribute 9 | { 10 | public string Route { get; set; } 11 | } 12 | 13 | [AttributeUsage(AttributeTargets.Method)] 14 | public class GetAttribute : Attribute 15 | { 16 | public string Route { get; set; } 17 | } 18 | 19 | [AttributeUsage(AttributeTargets.Method)] 20 | public class DelAttribute : Attribute 21 | { 22 | public string Route { get; set; } 23 | } 24 | 25 | [AttributeUsage(AttributeTargets.Method)] 26 | public class PutAttribute : Attribute 27 | { 28 | public string Route { get; set; } 29 | } 30 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = true)] 31 | public class CHeaderAttribute : Attribute 32 | { 33 | public CHeaderAttribute() 34 | { 35 | 36 | } 37 | public CHeaderAttribute(string name) 38 | { 39 | Name = name; 40 | } 41 | public CHeaderAttribute(string name, string value) 42 | { 43 | Name = name; 44 | Value = value; 45 | } 46 | public string Name { get; set; } 47 | 48 | public string Value { get; set; } 49 | } 50 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Interface, AllowMultiple = true)] 51 | public class CQueryAttribute : Attribute 52 | { 53 | public CQueryAttribute() 54 | { 55 | 56 | } 57 | 58 | public CQueryAttribute(string name) 59 | { 60 | Name = name; 61 | } 62 | public CQueryAttribute(string name, string value) 63 | { 64 | Name = name; 65 | Value = value; 66 | } 67 | 68 | public string Name { get; set; } 69 | 70 | public string Value { get; set; } 71 | } 72 | 73 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 74 | public class RequestMaxRPS : Attribute 75 | { 76 | public RequestMaxRPS(int value) 77 | { 78 | Value = value; 79 | } 80 | 81 | public int Value { get; set; } 82 | } 83 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 84 | public class RouteMapAttribute : Attribute 85 | { 86 | 87 | public RouteMapAttribute(string url) 88 | { 89 | Url = url; 90 | } 91 | 92 | public string Url { get; set; } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/PostFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class PostFile 8 | { 9 | public string FileName { get; internal set; } 10 | 11 | public string ContentType { get; internal set; } 12 | 13 | public string CharSet { get; set; } 14 | 15 | public System.IO.Stream Data { get; internal set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | Release 8 | Any CPU 9 | C:\localnuget 10 | FileSystem 11 | 12 | -------------------------------------------------------------------------------- /src/Properties/PublishProfiles/FolderProfile.pubxml.user: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | True|2022-11-21T11:20:42.1001202Z;True|2022-11-21T19:09:45.3036652+08:00;True|2022-11-17T10:32:49.8322910+08:00;True|2022-11-17T10:20:23.9129917+08:00;True|2022-11-16T15:10:03.5199946+08:00;True|2022-06-07T19:20:14.1057577+08:00;True|2022-06-01T13:06:13.5070879+08:00;True|2022-06-01T13:05:16.6689441+08:00;True|2022-05-27T19:39:09.3609245+08:00;True|2022-05-11T12:20:30.4528149+08:00;True|2022-05-11T12:11:51.0408354+08:00;True|2022-05-11T12:03:12.5969654+08:00;True|2022-04-30T14:59:44.4055442+08:00;True|2022-02-24T13:26:14.1536154+08:00;True|2022-02-24T13:25:15.1625893+08:00;True|2022-02-18T20:00:04.6422454+08:00;True|2022-02-14T19:52:18.5891643+08:00;True|2022-02-14T19:37:15.2862284+08:00;True|2022-02-11T15:17:52.3935173+08:00;True|2022-02-11T15:12:38.4651377+08:00;True|2022-02-08T19:14:53.8108823+08:00;True|2022-02-08T19:13:40.3788720+08:00;True|2022-02-08T18:47:15.5500865+08:00;True|2022-02-08T18:41:21.1176248+08:00;False|2022-02-08T18:39:14.6964646+08:00;True|2021-10-11T21:24:14.8886166+08:00;True|2021-10-10T12:59:53.7874198+08:00;True|2021-09-17T11:03:57.5658942+08:00;True|2021-09-16T22:16:49.9328108+08:00;True|2021-09-16T22:12:02.5285439+08:00;True|2021-09-16T21:22:44.7974152+08:00;True|2021-09-16T20:59:35.0137932+08:00;True|2021-09-06T20:21:21.0413421+08:00;True|2021-09-06T20:20:37.8011537+08:00; 8 | 9 | -------------------------------------------------------------------------------- /src/QueryString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class QueryString 8 | { 9 | 10 | public QueryString(Data.IDataContext dataContext) 11 | { 12 | mDataContext = dataContext; 13 | } 14 | 15 | private Data.IDataContext mDataContext; 16 | 17 | 18 | public string this[string name] 19 | { 20 | get 21 | { 22 | return GetValue(name); 23 | } 24 | } 25 | 26 | private string GetValue(string name) 27 | { 28 | string result; 29 | mDataContext.TryGetString(name, out result); 30 | return result; 31 | 32 | 33 | } 34 | 35 | internal void Add(string name, string value) 36 | { 37 | mDataContext.SetValue(name, System.Web.HttpUtility.UrlDecode(value)); 38 | 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Route/RouteAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace BeetleX.FastHttpApi 7 | { 8 | [AttributeUsage(AttributeTargets.Method)] 9 | public class RouteTemplateAttribute : Attribute 10 | { 11 | public RouteTemplateAttribute(string template) 12 | { 13 | Templete = template; 14 | } 15 | public string Templete { get; set; } 16 | 17 | public string Analysis(string parent) 18 | { 19 | if (string.IsNullOrEmpty(Templete)) 20 | return null; 21 | //if (string.IsNullOrEmpty(parent)) 22 | // parent = "/"; 23 | //if (parent[parent.Length - 1] != '/') 24 | // parent += "/"; 25 | if (!Regex.IsMatch(Templete, @"(\{[A-Za-z0-9_]+\})")) 26 | { 27 | return null; 28 | 29 | } 30 | if (string.IsNullOrEmpty(parent)) 31 | return Templete; 32 | return parent + Templete; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Route/RouteGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Linq; 5 | namespace BeetleX.FastHttpApi 6 | { 7 | 8 | public class RouteGroup 9 | { 10 | 11 | public RouteGroup() 12 | { 13 | mRoutes = new List(); 14 | } 15 | 16 | public bool Cached { get; set; } = true; 17 | 18 | public string Path { get; set; } 19 | 20 | public int PathLevel { get; set; } 21 | 22 | private List mRoutes; 23 | 24 | private UrlRoute[] mMatchRoute = new UrlRoute[0]; 25 | 26 | public UrlRoute[] Routes => mMatchRoute; 27 | 28 | public void Remove(UrlRoute route) 29 | { 30 | mRoutes.RemoveAll(p => string.Compare(p.ID, route.ID, true) == 0); 31 | Refresh(); 32 | } 33 | 34 | private void Refresh() 35 | { 36 | mMatchRoute = (from a in mRoutes orderby a.GetPrefixCode() descending select a).ToArray(); 37 | foreach (var item in mMatchRoute) 38 | { 39 | if (item.Prefix != null && item.Prefix.Type == UrlPrefix.PrefixType.Param) 40 | { 41 | Cached = false; 42 | return; 43 | } 44 | } 45 | Cached = true; 46 | } 47 | 48 | 49 | public void Add(UrlRoute route) 50 | { 51 | for (int i = 0; i < mRoutes.Count; i++) 52 | { 53 | if (string.Compare(mRoutes[i].ID, route.ID, true) == 0) 54 | { 55 | mRoutes[i] = route; 56 | return; 57 | } 58 | } 59 | mRoutes.Add(route); 60 | Refresh(); 61 | } 62 | 63 | public UrlRoute Match(string url, ref RouteMatchResult result, Dictionary parameters, string ext, HttpRequest request) 64 | { 65 | 66 | var items = mMatchRoute; 67 | for (int i = 0; i < items.Length; i++) 68 | { 69 | UrlRoute urlRoute = items[i]; 70 | if (string.Compare(urlRoute.Ext, ext, true) == 0) 71 | { 72 | if (urlRoute.Prefix != null) 73 | { 74 | var prefixValue = urlRoute.Prefix.GetPrefix(request); 75 | if (string.Compare(prefixValue, urlRoute.Prefix.Value, true) != 0) 76 | continue; 77 | } 78 | if (urlRoute.Match(url, parameters)) 79 | { 80 | result.Ext = urlRoute.ReExt; 81 | result.RewriteUrl = urlRoute.GetRewriteUrl(parameters); 82 | return urlRoute; 83 | } 84 | } 85 | } 86 | return null; 87 | } 88 | 89 | public override string ToString() 90 | { 91 | return Path; 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Route/RouteMatchResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class RouteMatchResult 8 | { 9 | public string RewriteUrl; 10 | public string RewriteUrlLower; 11 | public string Ext; 12 | public bool HasQueryString = false; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/RpsLimit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class RpsLimit 8 | { 9 | public RpsLimit(int max) 10 | { 11 | mMax = max; 12 | } 13 | 14 | private int mMax; 15 | 16 | private long mRpsCount; 17 | 18 | private long mLastRpsTime; 19 | 20 | public void SetMaxRpx(int value) 21 | { 22 | this.mMax = value; 23 | } 24 | 25 | public bool Check(int max = 0) 26 | { 27 | if (max > 0) 28 | mMax = max; 29 | if (mMax <= 0) 30 | return false; 31 | else 32 | { 33 | mRpsCount = System.Threading.Interlocked.Increment(ref mRpsCount); 34 | long now = TimeWatch.GetElapsedMilliseconds(); 35 | long time = now - mLastRpsTime; 36 | if (time >= 1000) 37 | { 38 | System.Threading.Interlocked.Exchange(ref mRpsCount, 0); 39 | System.Threading.Interlocked.Exchange(ref mLastRpsTime, now); 40 | } 41 | else 42 | { 43 | if (mRpsCount > mMax) 44 | return true; 45 | } 46 | } 47 | return false; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/SameSiteType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public enum SameSiteType 8 | { 9 | None, 10 | Strict, 11 | Lax 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ServerStatusController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [Controller(BaseUrl = "/")] 8 | public class ServerStatusController 9 | { 10 | [DefaultJsonResultFilter] 11 | public object __ServerStatus(IHttpContext context) 12 | { 13 | if (context.Server.ServerCounter != null) 14 | { 15 | return context.Server.ServerCounter.Next(); 16 | } 17 | return new ServerCounter.ServerStatus(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SessionControllerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Collections.Concurrent; 5 | namespace BeetleX.FastHttpApi 6 | { 7 | class SessionControllerFactory : IDisposable 8 | { 9 | 10 | const string FACTORY_TAG = "__CONTROLLER_FACTORY_TAG"; 11 | 12 | private ConcurrentDictionary mControllers = new ConcurrentDictionary(); 13 | 14 | private IServer mServer; 15 | 16 | public static SessionControllerFactory GetFactory(ISession session) 17 | { 18 | SessionControllerFactory factory = (SessionControllerFactory)session[FACTORY_TAG]; 19 | if (factory == null) 20 | { 21 | factory = new SessionControllerFactory(); 22 | factory.mServer = session.Server; 23 | session[FACTORY_TAG] = factory; 24 | } 25 | return factory; 26 | } 27 | 28 | public object this[string name] 29 | { 30 | get 31 | { 32 | mControllers.TryGetValue(name, out object result); 33 | return result; 34 | } 35 | set 36 | { 37 | mControllers[name] = value; 38 | } 39 | } 40 | 41 | 42 | public static void DisposedFactory(ISession session) 43 | { 44 | SessionControllerFactory factory = (SessionControllerFactory)session[FACTORY_TAG]; 45 | factory?.Dispose(); 46 | } 47 | 48 | public void Dispose() 49 | { 50 | foreach (var item in mControllers.Values) 51 | { 52 | if (item is IDisposable disposable) 53 | { 54 | try 55 | { 56 | disposable?.Dispose(); 57 | } 58 | catch (Exception e_) 59 | { 60 | if (mServer.EnableLog(EventArgs.LogType.Error)) 61 | { 62 | mServer.Log(EventArgs.LogType.Error, null, 63 | $"HTTP {item} controller disposed error {e_.Message}@{e_.StackTrace}"); 64 | } 65 | } 66 | } 67 | } 68 | mControllers.Clear(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/SkipFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 8 | public class SkipFilterAttribute : Attribute 9 | { 10 | 11 | public SkipFilterAttribute(params Type[] types) 12 | { 13 | Types = types; 14 | if (Types == null) 15 | Types = new Type[0]; 16 | } 17 | 18 | public Type[] Types { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/StaticResurce/EventFileResponseArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.StaticResurce 6 | { 7 | public class EventFileResponseArgs : System.EventArgs 8 | { 9 | public HttpRequest Request { get; internal set; } 10 | 11 | public HttpResponse Response { get; internal set; } 12 | 13 | public FileResource Resource { get; set; } 14 | 15 | public FileContentType ContentType { get; set; } 16 | 17 | public bool Cancel { get; set; } = false; 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/StaticResurce/EventFindFileArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.StaticResurce 6 | { 7 | 8 | public class EventFindFileArgs : System.EventArgs 9 | { 10 | public string File { get; set; } 11 | 12 | public HttpRequest Request { get; internal set; } 13 | 14 | public string Url { get; internal set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/StaticResurce/FileBlock.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO.Compression; 5 | using System.Text; 6 | 7 | namespace BeetleX.FastHttpApi.StaticResurce 8 | { 9 | public class FileBlock:IDataResponse 10 | { 11 | public FileBlock(FileResource rec) 12 | { 13 | Offset = 0; 14 | Size = 1024 * 8; 15 | Pages = rec.Length / Size; 16 | if (rec.Length % Size > 0) 17 | { 18 | Pages++; 19 | } 20 | mFileResource = rec; 21 | LoadData(); 22 | 23 | 24 | GZip = false; 25 | } 26 | 27 | public bool GZip { get; set; } 28 | 29 | private FileResource mFileResource; 30 | 31 | private GZipStream gZipStream; 32 | 33 | private void LoadData() 34 | { 35 | int roffset; 36 | Data = mFileResource.GetBlodk(Offset, Size, out roffset); 37 | Offset = roffset; 38 | 39 | } 40 | 41 | public int Size; 42 | 43 | public int Offset; 44 | 45 | public int Pages; 46 | 47 | public ArraySegment Data; 48 | 49 | public FileBlock Next() 50 | { 51 | mFileResource.Recovery(Data.Array); 52 | if (Offset < mFileResource.Length) 53 | { 54 | LoadData(); 55 | return this; 56 | } 57 | return null; 58 | } 59 | void IDataResponse.Write(PipeStream stream) 60 | { 61 | if (GZip) 62 | { 63 | var mb = stream.Allocate(16); 64 | stream.Write(HeaderTypeFactory.LINE_BYTES); 65 | int len = stream.CacheLength; 66 | if (gZipStream == null) 67 | gZipStream = new GZipStream(stream, CompressionMode.Compress, true); 68 | using (stream.LockFree()) 69 | { 70 | gZipStream.Write(Data.Array, Data.Offset, Data.Count); 71 | gZipStream.Flush(); 72 | if (Offset == mFileResource.Length) 73 | { 74 | if (gZipStream != null) 75 | { 76 | 77 | gZipStream.Dispose(); 78 | 79 | } 80 | } 81 | } 82 | string lenstr = (stream.CacheLength - len).ToString("X"); 83 | mb.Full(Encoding.UTF8.GetBytes(lenstr.PadRight(16))); 84 | } 85 | else 86 | { 87 | int len = Data.Count; 88 | stream.Write(len.ToString("X")); 89 | stream.Write(HeaderTypeFactory.LINE_BYTES); 90 | stream.Write(Data.Array, Data.Offset, Data.Count); 91 | } 92 | stream.WriteLine(""); 93 | if (Offset == mFileResource.Length) 94 | { 95 | stream.Write(HeaderTypeFactory.CHUNKED_BYTES); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/StaticResurce/FileContentType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.StaticResurce 6 | { 7 | public class FileContentType 8 | { 9 | public FileContentType(string filetype) 10 | { 11 | Ext = filetype; 12 | ContentType = ContentTypes.GetContentType(Ext); 13 | } 14 | 15 | public string Ext { get; set; } 16 | 17 | public string ContentType { get; set; } 18 | 19 | public override string ToString() 20 | { 21 | return string.Format(".{0},{1}", Ext, this.ContentType); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Validations/IValidationOutputHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Reflection; 5 | namespace BeetleX.FastHttpApi.Validations 6 | { 7 | public interface IValidationOutputHandler 8 | { 9 | void Execute(IHttpContext context, IActionResultHandler handler, ValidationBase validation, ParameterInfo parameterInfo); 10 | } 11 | 12 | public class ValidationOutputHandler : IValidationOutputHandler 13 | { 14 | public void Execute(IHttpContext context, IActionResultHandler handler, ValidationBase validation, ParameterInfo parameterInfo) 15 | { 16 | ActionResult actionResult = new ActionResult(validation.Code, validation.GetResultMessage(parameterInfo.Name)); 17 | handler.Success(actionResult); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/Validations/Regex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.Validations 6 | { 7 | public class Regex : ValidationBase 8 | { 9 | 10 | public Regex(string pattern) 11 | { 12 | Pattern = pattern; 13 | } 14 | 15 | public string Pattern { get; set; } 16 | 17 | public override bool Execute(object data) 18 | { 19 | string value = (string)data; 20 | if (!Non && string.IsNullOrEmpty(value)) 21 | return true; 22 | if (string.IsNullOrEmpty(value)) 23 | return false; 24 | return System.Text.RegularExpressions.Regex.IsMatch(value, Pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase); 25 | } 26 | 27 | public override string GetResultMessage(string parameter) 28 | { 29 | return $"The '{parameter}' parameter value not match'{Pattern}'"; 30 | } 31 | } 32 | 33 | public class HourFormater : Regex 34 | { 35 | public HourFormater() : base(@"^([--:\w?@%&+~#=]*\.[a-z]{2,4}\/{0,2})((?:[?&](?:\w+)=(?:\w+))+|[--:\w?@%&+~#=]+)?$") 36 | { 37 | 38 | } 39 | public override string GetResultMessage(string parameter) 40 | { 41 | return $"The '{parameter}' parameter value not a hour data format"; 42 | } 43 | } 44 | 45 | 46 | public class UrlFormater : Regex 47 | { 48 | public UrlFormater() : base(@"^([--:\w?@%&+~#=]*\.[a-z]{2,4}\/{0,2})((?:[?&](?:\w+)=(?:\w+))+|[--:\w?@%&+~#=]+)?$") { } 49 | public override string GetResultMessage(string parameter) 50 | { 51 | return $"The '{parameter}' parameter value not a url data format"; 52 | } 53 | } 54 | 55 | 56 | public class PasswordFormater : Regex 57 | { 58 | public PasswordFormater() : base(@"^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$") { } 59 | public override string GetResultMessage(string parameter) 60 | { 61 | return $"The '{parameter}' parameter value not a password data format"; 62 | } 63 | } 64 | 65 | 66 | public class PhoneFormater : Regex 67 | { 68 | public PhoneFormater() : base(@"^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$") 69 | { 70 | 71 | 72 | } 73 | public override string GetResultMessage(string parameter) 74 | { 75 | return $"The '{parameter}' parameter value not a phone data format"; 76 | } 77 | } 78 | 79 | 80 | public class MPhoneFormater : Regex 81 | { 82 | public MPhoneFormater() : base(@"^(13\d|14[579]|15[^4\D]|17[^49\D]|18\d)\d{8}$") { } 83 | 84 | public override string GetResultMessage(string parameter) 85 | { 86 | return $"The '{parameter}' parameter value not a mobile phone data format"; 87 | } 88 | } 89 | 90 | 91 | public class IDCardFormater : Regex 92 | { 93 | 94 | public IDCardFormater() : base(@"^\d{17}[0-9Xx]|\d{15}$") { } 95 | 96 | public override string GetResultMessage(string parameter) 97 | { 98 | return $"The '{parameter}' parameter value not a identity card data format"; 99 | } 100 | } 101 | 102 | 103 | public class DateFormater : Regex 104 | { 105 | public DateFormater() : base(@"^(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)$") 106 | { 107 | 108 | } 109 | public override string GetResultMessage(string parameter) 110 | { 111 | return $"The '{parameter}' parameter value not a date data format"; 112 | } 113 | } 114 | 115 | 116 | public class EmailFormater : Regex 117 | { 118 | public EmailFormater() : base(@"^([A-Z|a-z|0-9](\.|_){0,1})+[A-Z|a-z|0-9]\@([A-Z|a-z|0-9])+((\.){0,1}[A-Z|a-z|0-9]){2,3}\.[a-z]{2,3}$") { } 119 | public override string GetResultMessage(string parameter) 120 | { 121 | return $"The '{parameter}' parameter value not a email data format"; 122 | } 123 | } 124 | 125 | public class IPFormater : Regex 126 | { 127 | public IPFormater() : base(@"^(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))$") { } 128 | public override string GetResultMessage(string parameter) 129 | { 130 | return $"The '{parameter}' parameter value not a ip address data format"; 131 | } 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/Validations/ValidationBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.Validations 6 | { 7 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] 8 | public abstract class ValidationBase : Attribute 9 | { 10 | 11 | public bool Non { get; set; } = true; 12 | 13 | public int Code { get; set; } = 412; 14 | 15 | public virtual string GetResultMessage(string parameter) 16 | { 17 | 18 | return ""; 19 | } 20 | 21 | public virtual bool Execute(object data) 22 | { 23 | return Non && data != null; 24 | } 25 | } 26 | 27 | public class Non : ValidationBase 28 | { 29 | public override string GetResultMessage(string parameter) 30 | { 31 | return $"The '{parameter}' parameter value cannot be null!"; 32 | } 33 | } 34 | 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/VirtualFolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi 6 | { 7 | public class VirtualFolder 8 | { 9 | public string Folder { get; set; } 10 | 11 | public string Path { get; set; } 12 | 13 | public void Verify() 14 | { 15 | if (Folder[Folder.Length - 1] != '/') 16 | Folder += "/"; 17 | if (Folder[0] != '/') 18 | Folder = "/"+Folder; 19 | if (Path[Path.Length - 1] != System.IO.Path.DirectorySeparatorChar) 20 | { 21 | Path += System.IO.Path.DirectorySeparatorChar; 22 | } 23 | if (!System.IO.Directory.Exists(Path)) 24 | { 25 | throw new Exception($"Set virtual folder error {Path} path not found!"); 26 | } 27 | } 28 | 29 | public string Match(string file) 30 | { 31 | if (file.IndexOf(Folder, StringComparison.OrdinalIgnoreCase) >= 0) 32 | { 33 | return Path + file.Substring(Folder.Length); 34 | } 35 | return null; 36 | } 37 | 38 | public string Match(HttpRequest request) 39 | { 40 | return Match(request.BaseUrl); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/WebSockets/DataPackeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.WebSockets 6 | { 7 | public enum DataPacketType : byte 8 | { 9 | continuation = 0x0, 10 | text = 0x1, 11 | binary = 0x2, 12 | non_control3 = 0x3, 13 | non_control4 = 0x4, 14 | non_control5 = 0x5, 15 | non_control6 = 0x6, 16 | non_control7 = 0x7, 17 | connectionClose = 0x8, 18 | ping = 0x9, 19 | pong = 0xA, 20 | controlB = 0xB, 21 | controlE = 0xE, 22 | controlF = 0xF 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/WebSockets/EventWebSocketReceive.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace BeetleX.FastHttpApi.WebSockets 8 | { 9 | 10 | 11 | public class WebSocketReceiveArgs : System.EventArgs 12 | { 13 | public ISession Sesson { get; internal set; } 14 | 15 | public HttpRequest Request { get; internal set; } 16 | 17 | public DataFrame Frame { get; internal set; } 18 | 19 | public IWebSocketServer Server { get; internal set; } 20 | 21 | public void Response(DataFrame data) 22 | { 23 | Sesson.Send(data); 24 | } 25 | 26 | public void ResponseBinary(object data) 27 | { 28 | var frame = CreateFrame(data); 29 | frame.Type = DataPacketType.binary; 30 | Response(frame); 31 | } 32 | 33 | public void ResponseText(object data) 34 | { 35 | var frame = CreateFrame(data); 36 | frame.Type = DataPacketType.text; 37 | Response(frame); 38 | } 39 | 40 | public DataFrame CreateFrame(object body = null) 41 | { 42 | var result = Server.CreateDataFrame(body); 43 | return result; 44 | } 45 | 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/WebSockets/IDataPacketSerializer.cs: -------------------------------------------------------------------------------- 1 | using BeetleX.Buffers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace BeetleX.FastHttpApi.WebSockets 7 | { 8 | public interface IDataFrameSerializer 9 | { 10 | object FrameDeserialize(DataFrame data, PipeStream stream,HttpRequest request); 11 | 12 | ArraySegment FrameSerialize(DataFrame packet, object body,HttpRequest request); 13 | 14 | void FrameRecovery(byte[] buffer); 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/WebSockets/IWebSocketServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.WebSockets 6 | { 7 | public interface IWebSocketServer 8 | { 9 | EventHandler WebSocketReceive { get; set; } 10 | 11 | DataFrame CreateDataFrame(object body = null); 12 | 13 | void SendToWebSocket(DataFrame data, Func filter = null); 14 | 15 | void SendToWebSocket(DataFrame data, params HttpRequest[] request); 16 | 17 | IList GetWebSockets(); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/WebSockets/WebSocketPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using BeetleX.Buffers; 6 | using BeetleX.EventArgs; 7 | 8 | namespace BeetleX.FastHttpApi.WebSockets 9 | { 10 | class WebSocketPacket : IPacket 11 | { 12 | public WebSocketPacket(HttpConfig serverConfig, IDataFrameSerializer dataPacketSerializer) 13 | { 14 | mServerConfig = serverConfig; 15 | mBodySerializer = new StringSerializer(); 16 | mDataPacketSerializer = dataPacketSerializer; 17 | } 18 | 19 | private IBodySerializer mBodySerializer; 20 | 21 | private IDataFrameSerializer mDataPacketSerializer; 22 | 23 | public EventHandler Completed { get; set; } 24 | 25 | private DataFrame mDataPacket; 26 | 27 | private bool mLodingDataPacket = false; 28 | 29 | public IPacket Clone() 30 | { 31 | return new WebSocketPacket(mServerConfig, mDataPacketSerializer); 32 | } 33 | 34 | private HttpConfig mServerConfig; 35 | 36 | private PacketDecodeCompletedEventArgs mCompletedArgs = new PacketDecodeCompletedEventArgs(); 37 | 38 | private HttpRequest mConnectionRequest; 39 | 40 | public void Decode(ISession session, Stream stream) 41 | { 42 | PipeStream pstream = stream.ToPipeStream(); 43 | if (pstream.Length > mServerConfig.MaxBodyLength) 44 | { 45 | session.Server.Log(LogType.Error, session, "http body too long!"); 46 | session.Dispose(); 47 | return; 48 | } 49 | START: 50 | if (!mLodingDataPacket) 51 | { 52 | if (mConnectionRequest == null) 53 | { 54 | mConnectionRequest = new HttpRequest(session, mBodySerializer); 55 | } 56 | if (mConnectionRequest.Read(pstream) == LoadedState.Completed) 57 | { 58 | mLodingDataPacket = true; 59 | Completed?.Invoke(this, mCompletedArgs.SetInfo(session, mConnectionRequest)); 60 | if (pstream.Length > 0) 61 | goto START; 62 | } 63 | } 64 | else 65 | { 66 | if (mDataPacket == null) 67 | { 68 | mDataPacket = new DataFrame(); 69 | mDataPacket.DataPacketSerializer = this.mDataPacketSerializer; 70 | } 71 | if (mDataPacket.Read(pstream) == DataPacketLoadStep.Completed) 72 | { 73 | DataFrame data = mDataPacket; 74 | mDataPacket = null; 75 | Completed?.Invoke(this, mCompletedArgs.SetInfo(session, data)); 76 | if (pstream.Length > 0) 77 | goto START; 78 | } 79 | } 80 | 81 | } 82 | 83 | public void Dispose() 84 | { 85 | 86 | } 87 | 88 | public void Encode(object data, ISession session, Stream stream) 89 | { 90 | OnEncode(session, data, stream); 91 | } 92 | 93 | private void OnEncode(ISession session, object data, System.IO.Stream stream) 94 | { 95 | PipeStream pstream = stream.ToPipeStream(); 96 | DataFrame dp = data as DataFrame; 97 | if (dp != null) 98 | { 99 | dp.Write(pstream); 100 | } 101 | else 102 | { 103 | HttpResponse response = (HttpResponse)data; 104 | response.Write(pstream); 105 | } 106 | 107 | } 108 | 109 | public byte[] Encode(object data, IServer server) 110 | { 111 | byte[] result = null; 112 | using (Buffers.PipeStream stream = new PipeStream(server.BufferPool, server.Config.LittleEndian, server.Config.Encoding)) 113 | { 114 | OnEncode(null, data, stream); 115 | stream.Position = 0; 116 | result = new byte[stream.Length]; 117 | stream.Read(result, 0, result.Length); 118 | } 119 | return result; 120 | } 121 | 122 | public ArraySegment Encode(object data, IServer server, byte[] buffer) 123 | { 124 | using (Buffers.PipeStream stream = new PipeStream(server.BufferPool, server.Config.LittleEndian, server.Config.Encoding)) 125 | { 126 | OnEncode(null, data, stream); 127 | stream.Position = 0; 128 | int count = (int)stream.Length; 129 | stream.Read(buffer, 0, count); 130 | return new ArraySegment(buffer, 0, count); 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/WebSockets/WebSocketToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BeetleX.FastHttpApi.WebSockets 6 | { 7 | public class WebSocketToken 8 | { 9 | public WebSocketToken() 10 | { 11 | 12 | } 13 | 14 | public HttpRequest ConnectionRequest { get; set; } 15 | 16 | public bool Connected { get; set; } 17 | 18 | 19 | } 20 | } 21 | --------------------------------------------------------------------------------