├── .gitattributes ├── .gitignore ├── Demos.sln ├── Demos.src └── OwinTest │ ├── Controllers │ ├── TestHub.cs │ └── ValuesController.cs │ ├── Default.html │ ├── JwsAdapter.cs │ ├── OwinTest.csproj │ ├── Properties │ └── AssemblyInfo.cs │ ├── README.md │ ├── Scripts │ ├── jquery-3.7.1.intellisense.js │ ├── jquery-3.7.1.js │ ├── jquery-3.7.1.min.js │ ├── jquery-3.7.1.min.map │ ├── jquery-3.7.1.slim.js │ ├── jquery-3.7.1.slim.min.js │ ├── jquery-3.7.1.slim.min.map │ ├── jquery.signalR-2.4.3.js │ └── jquery.signalR-2.4.3.min.js │ ├── Startup.cs │ ├── images │ └── jexus_logo_h32.png │ ├── packages.config │ ├── web.config │ └── web_socket.html ├── LICENSE ├── README.md ├── docs ├── manual.md └── manual_v8.md └── images └── jexus_logo_h32.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017.. cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | -------------------------------------------------------------------------------- /Demos.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35327.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinTest", "Demos.src\OwinTest\OwinTest.csproj", "{7FB28367-4C46-41A1-BC54-96104720A1BB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Debug|x64.ActiveCfg = Debug|Any CPU 21 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Debug|x64.Build.0 = Debug|Any CPU 22 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Debug|x86.ActiveCfg = Debug|Any CPU 23 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Debug|x86.Build.0 = Debug|Any CPU 24 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Release|x64.ActiveCfg = Release|Any CPU 27 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Release|x64.Build.0 = Release|Any CPU 28 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Release|x86.ActiveCfg = Release|Any CPU 29 | {7FB28367-4C46-41A1-BC54-96104720A1BB}.Release|x86.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {17C19C19-E039-455C-BEFD-72C002348527} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/Controllers/TestHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.SignalR; 2 | 3 | namespace OwinTest.Controllers 4 | { 5 | public class TestHub : Hub 6 | { 7 | 8 | public TestHub() { } 9 | 10 | public void SendMessage(string message) 11 | { 12 | 13 | Clients.All.RecvMessage(message); 14 | } 15 | 16 | 17 | 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /Demos.src/OwinTest/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Http; 3 | 4 | namespace OwinTest.Controllers 5 | { 6 | /// 7 | /// WebApi控制器:Values 8 | /// 9 | public class ValuesController : ApiController 10 | { 11 | 12 | static readonly List _values = new List { "value1", "value2", "value3" }; 13 | 14 | 15 | 16 | // GET api/values 17 | public IEnumerable Get() 18 | { 19 | return _values; 20 | } 21 | 22 | // GET api/values/5 23 | public string Get(int id) 24 | { 25 | if (id < 0 || id > _values.Count) return "NULL."; 26 | return _values[id]; 27 | } 28 | 29 | // POST api/values 30 | public void Post([FromBody] string value) 31 | { 32 | _values.Add(value); 33 | } 34 | 35 | // PUT api/values/5 36 | public void Put(int id, [FromBody] string value) 37 | { 38 | if (id < 0 || id > _values.Count) return; 39 | _values[id] = value; 40 | } 41 | 42 | // DELETE api/values/5 43 | public void Delete(int id) 44 | { 45 | if (id < 0 || id > _values.Count) return; 46 | _values.RemoveAt(id); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/Default.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | OwinTest Demo 6 | 7 | 8 | 51 | 52 | 53 | 54 | 55 |
56 | OWIN WEB APPLICATION DEMOs 57 |
58 | 59 | 63 | 64 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/JwsAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Owin.Builder; 4 | using System.Threading.Tasks; 5 | 6 | 7 | namespace OwinTest 8 | { 9 | /// 10 | /// Jexus OWIN适配器(也适用于TinyFox) 11 | /// 12 | public class JwsAdapter 13 | { 14 | /// 15 | /// Microsoft OWIN 处理管线入口函数 16 | /// 17 | static Func, Task> _owinAppFunc; 18 | 19 | /// 20 | /// 一个处于取消状态的任务对象 21 | /// 22 | readonly static Task _canceledTask; 23 | 24 | 25 | 26 | /// 27 | /// 静态构造函数 28 | /// 29 | static JwsAdapter() 30 | { 31 | var tcs = new TaskCompletionSource(); 32 | tcs.SetCanceled(); 33 | _canceledTask = tcs.Task; 34 | } 35 | 36 | /// 37 | /// 适本器构造函数 38 | /// 39 | public JwsAdapter() 40 | { 41 | //配置并构建OWIN处理管线 42 | var builder = new AppBuilder(); 43 | new Startup().Configuration(builder); 44 | _owinAppFunc = builder.Build(); 45 | } 46 | 47 | 48 | 49 | /// 50 | /// *** Jexus或TinyFox驱动OWIN所需要的关键函数 *** 51 | /// 每个请求到来,JWS/TinyFox都把请求打包成字典,通过这个函数提供给OWIN应用程序具体处理 52 | /// 53 | /// 新请求的环境字典 54 | /// 返回一个正在运行或已经完成的任务,返回取消表示不属于本OWIN,返回null表示中止处理 55 | public Task OwinMain(IDictionary env) 56 | { 57 | 58 | if (_owinAppFunc == null) return null; 59 | 60 | //浏览器请求的路径 61 | var req_path = env["owin.RequestPath"] as string; 62 | 63 | 64 | //属于本OWIN应用处理的请求交给OWIN处理,其它的返回一个取消状态的Task。 65 | 66 | var isApiPath = req_path.StartsWith("/api/"); 67 | var isSignalRPath = req_path.StartsWith("/signalr/"); 68 | 69 | if (isApiPath || isSignalRPath) 70 | { 71 | //交给OWIN管道进行具体处理 72 | return _owinAppFunc(env); 73 | } 74 | else 75 | { 76 | //返回一个取消了的task对象,表示不属于本OWIN处理的路径 77 | return _canceledTask; 78 | } 79 | } 80 | 81 | } 82 | } -------------------------------------------------------------------------------- /Demos.src/OwinTest/OwinTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {7FB28367-4C46-41A1-BC54-96104720A1BB} 11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 12 | Library 13 | Properties 14 | OwinTest 15 | OwinTest 16 | v4.6.2 17 | false 18 | true 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | true 29 | full 30 | false 31 | bin\ 32 | DEBUG;TRACE 33 | prompt 34 | 4 35 | true 36 | 37 | 38 | true 39 | pdbonly 40 | true 41 | bin\ 42 | TRACE 43 | prompt 44 | 4 45 | true 46 | 47 | 48 | 49 | ..\..\packages\Microsoft.AspNet.SignalR.Core.2.4.3\lib\net45\Microsoft.AspNet.SignalR.Core.dll 50 | 51 | 52 | ..\..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.4.3\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll 53 | 54 | 55 | 56 | ..\..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll 57 | 58 | 59 | ..\..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll 60 | 61 | 62 | ..\..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll 63 | 64 | 65 | ..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 66 | 67 | 68 | ..\..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll 69 | 70 | 71 | ..\..\packages\Owin.1.0\lib\net40\Owin.dll 72 | 73 | 74 | 75 | ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 76 | 77 | 78 | 79 | 80 | 81 | 82 | ..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll 83 | 84 | 85 | ..\..\packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll 86 | 87 | 88 | 89 | ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 90 | 91 | 92 | ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll 93 | 94 | 95 | 96 | ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll 97 | 98 | 99 | 100 | ..\..\packages\Microsoft.AspNet.WebApi.Core.5.3.0\lib\net45\System.Web.Http.dll 101 | 102 | 103 | ..\..\packages\Microsoft.AspNet.WebApi.Owin.5.3.0\lib\net45\System.Web.Http.Owin.dll 104 | 105 | 106 | ..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.3.0\lib\net45\System.Web.Http.WebHost.dll 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 10.0 150 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | True 163 | 164 | 165 | 166 | 167 | 173 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息是通过以下项进行控制的 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("OwinTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("OwinTest")] 13 | [assembly: AssemblyCopyright("版权所有(C) 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 将使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要 19 | // 从 COM 访问此程序集中的某个类型,请针对该类型将 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID 23 | [assembly: Guid("0d04c733-0c1b-475d-b4a7-65b2243a2f2b")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 内部版本号 30 | // 修订版本 31 | // 32 | // 你可以指定所有值,也可以让修订版本和内部版本号采用默认值, 33 | // 方法是按如下所示使用 "*": 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/README.md: -------------------------------------------------------------------------------- 1 | # OWIN Web应用程序示例 2 | 3 | 这是一个不依赖 ASP.NET 框架环境的“纯OWIN”Web应用程序示例,可直接在 Jexus 和 TinyFox 上运行,简单调整后也能在 IIS 上运行。 4 | 5 | **一、示例目的** 6 | 7 | - 展示如何开发一个具有 OwinMain 方法的“适配器”,让 Jexus 驱动基于微软OWIN接口标准开发的 WEB 程序。 8 | - 了解基于OWIN标准的WEB程序的开发基本特点。 9 | 10 | **二、示例功能:** 11 | 12 | - WebApi 13 | - SignalR 14 | 15 | **三、部署过程:** 16 | 17 | 1. 在VS中,将该项目发布到开发机的某个目标文件夹中; 18 | 2. 将目标文件夹中的所有文件上传到 Jexus 所在的服务器上的某个网站文件夹内; 19 | 3. 在 Jexus 的siteconf文件夹中新建一个网站配置文件,将网站文件夹路径填到root项中; 20 | 4. 在上一步的网站配置文件中添加一行,设定含用 OwinMain 方法的“适配器”所在的dll名称,如:OwinMain=OwinTest.dll 21 | 5. 重启 jexus 或单独启动这个网站 22 | 6. 尝试访问这个网站 23 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/Scripts/jquery-3.7.1.slim.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.7.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween | (c) OpenJS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},m=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||m).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),b=new RegExp(ge+"|>"),A=new RegExp(g),D=new RegExp("^"+t+"$"),N={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+d),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},L=/^(?:input|select|textarea|button)$/i,j=/^h\d$/i,O=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,P=/[+~]/,H=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),q=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},R=function(){V()},M=K(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{E.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){E={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(V(e),e=e||C,T)){if(11!==d&&(u=O.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return E.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return E.call(n,a),n}else{if(u[2])return E.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return E.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||p&&p.test(t))){if(c=t,f=e,1===d&&(b.test(t)||m.test(t))){(f=P.test(t)&&X(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=k)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+G(l[o]);c=l.join(",")}try{return E.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function B(e){return e[k]=!0,e}function F(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function $(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&M(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function U(a){return B(function(o){return o=+o,B(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function X(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=C&&9===n.nodeType&&n.documentElement&&(r=(C=n).documentElement,T=!ce.isXMLDoc(C),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=C&&(t=C.defaultView)&&t.top!==t&&t.addEventListener("unload",R),le.getById=F(function(e){return r.appendChild(e).id=ce.expando,!C.getElementsByName||!C.getElementsByName(ce.expando).length}),le.disconnectedMatch=F(function(e){return i.call(e,"*")}),le.scope=F(function(){return C.querySelectorAll(":scope")}),le.cssHas=F(function(){try{return C.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(x.filter.ID=function(e){var t=e.replace(H,q);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&T){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(H,q);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&T){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},x.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&T)return t.getElementsByClassName(e)},p=[],F(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||p.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+k+"-]").length||p.push("~="),e.querySelectorAll("a#"+k+"+*").length||p.push(".#.+[+~]"),e.querySelectorAll(":checked").length||p.push(":checked"),(t=C.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&p.push(":enabled",":disabled"),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||p.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||p.push(":has"),p=p.length&&new RegExp(p.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===C||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),C}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),T&&!h[t+" "]&&(!p||!p.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(H,q),e[3]=(e[3]||e[4]||e[5]||"").replace(H,q),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return N.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&A.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(H,q).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||E,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:k.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:m,!0)),C.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=m.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,E=ce(m);var S=/^(?:parents|prev(?:Until|All))/,A={children:!0,contents:!0,next:!0,prev:!0};function D(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;re=m.createDocumentFragment().appendChild(m.createElement("div")),(be=m.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),re.appendChild(be),le.checkClone=re.cloneNode(!0).cloneNode(!0).lastChild.checked,re.innerHTML="",le.noCloneChecked=!!re.cloneNode(!0).lastChild.defaultValue,re.innerHTML="",le.option=!!re.lastChild;var Te={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Ee(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function ke(e,t){for(var n=0,r=e.length;n",""]);var Se=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Me(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function We(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n
",2===yt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=m.implementation.createHTMLDocument("")).createElement("base")).href=m.location.href,t.head.appendChild(r)):t=m),o=!n&&[],(i=C.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||K})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return R(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Qe(le.pixelPosition,function(e,t){if(t)return t=Ve(e,n),$e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return R(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0=0;u--)f=t[u],n.type(f)==="string"&&r.transports[f]||(i.log("Invalid transport: "+f+", removing it from the transports list."),t.splice(u,1));t.length===0&&(i.log("No transports remain within the specified transport array."),t=null)}else if(r.transports[t]||t==="auto"){if(t==="auto"&&r._.ieVersion<=8)return["longPolling"]}else i.log("Invalid transport: "+t.toString()+"."),t=null;return t}function b(n){return n==="http:"?80:n==="https:"?443:void 0}function a(n,t){return t.match(/:\d+$/)?t:t+":"+b(n)}function k(t,i){var u=this,r=[];u.tryBuffer=function(i){return t.state===n.signalR.connectionState.connecting?(r.push(i),!0):!1};u.drain=function(){if(t.state===n.signalR.connectionState.connected)while(r.length>0)i(r.shift())};u.clear=function(){r=[]}}var f={nojQuery:"jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",noTransportOnInit:"No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.",errorOnNegotiate:"Error during negotiation request.",stoppedWhileLoading:"The connection was stopped during page load.",stoppedWhileNegotiating:"The connection was stopped during the negotiate request.",errorParsingNegotiateResponse:"Error parsing negotiate response.",errorRedirectionExceedsLimit:"Negotiate redirection limit exceeded.",errorDuringStartRequest:"Error during start request. Stopping the connection.",errorFromServer:"Error message received from the server: '{0}'.",stoppedDuringStartRequest:"The connection was stopped during the start request.",errorParsingStartResponse:"Error parsing start response: '{0}'. Stopping the connection.",invalidStartResponse:"Invalid start response: '{0}'. Stopping the connection.",protocolIncompatible:"You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",aspnetCoreSignalrServer:"Detected a connection attempt to an ASP.NET Core SignalR Server. This client only supports connecting to an ASP.NET SignalR Server. See https://aka.ms/signalr-core-differences for details.",sendFailed:"Send failed.",parseFailed:"Failed at parsing response: {0}",longPollFailed:"Long polling request failed.",eventSourceFailedToConnect:"EventSource failed to connect.",eventSourceError:"Error raised by EventSource",webSocketClosed:"WebSocket closed.",pingServerFailedInvalidResponse:"Invalid ping response when pinging server: '{0}'.",pingServerFailed:"Failed to ping server.",pingServerFailedStatusCode:"Failed to ping server. Server responded with status code {0}, stopping the connection.",pingServerFailedParse:"Failed to parse ping server response, stopping the connection.",noConnectionTransport:"Connection is in an invalid state, there is no transport active.",webSocketsInvalidState:"The Web Socket transport is in an invalid state, transitioning into reconnecting.",reconnectTimeout:"Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",reconnectWindowTimeout:"The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection.",jsonpNotSupportedWithAccessToken:"The JSONP protocol does not support connections that require a Bearer token to connect, such as the Azure SignalR Service."};if(typeof n!="function")throw new Error(f.nojQuery);var r,h,o=t.document.readyState==="complete",e=n(t),c="__Negotiate Aborted__",u={onStart:"onStart",onStarting:"onStarting",onReceived:"onReceived",onError:"onError",onConnectionSlow:"onConnectionSlow",onReconnecting:"onReconnecting",onReconnect:"onReconnect",onStateChanged:"onStateChanged",onDisconnect:"onDisconnect"},v=function(n,i){if(i!==!1){var r;typeof t.console!="undefined"&&(r="["+(new Date).toTimeString()+"] SignalR: "+n,t.console.debug?t.console.debug(r):t.console.log&&t.console.log(r))}},s=function(t,i,r){return i===t.state?(t.state=r,n(t).triggerHandler(u.onStateChanged,[{oldState:i,newState:r}]),!0):!1},y=function(n){return n.state===r.connectionState.disconnected},l=function(n){return n._.keepAliveData.activated&&n.transport.supportsKeepAlive(n)},p=function(i){var f,e;i._.configuredStopReconnectingTimeout||(e=function(t){var i=r._.format(r.resources.reconnectTimeout,t.disconnectTimeout);t.log(i);n(t).triggerHandler(u.onError,[r._.error(i,"TimeoutException")]);t.stop(!1,!1)},i.reconnecting(function(){var n=this;n.state===r.connectionState.reconnecting&&(f=t.setTimeout(function(){e(n)},n.disconnectTimeout))}),i.stateChanged(function(n){n.oldState===r.connectionState.reconnecting&&t.clearTimeout(f)}),i._.configuredStopReconnectingTimeout=!0)};if(r=function(n,t,i){return new r.fn.init(n,t,i)},r._={defaultContentType:"application/x-www-form-urlencoded; charset=UTF-8",ieVersion:function(){var i,n;return t.navigator.appName==="Microsoft Internet Explorer"&&(n=/MSIE ([0-9]+\.[0-9]+)/.exec(t.navigator.userAgent),n&&(i=t.parseFloat(n[1]))),i}(),error:function(n,t,i){var r=new Error(n);return r.source=t,typeof i!="undefined"&&(r.context=i),r},transportError:function(n,t,r,u){var f=this.error(n,r,u);return f.transport=t?t.name:i,f},format:function(){for(var t=arguments[0],n=0;n<\/script>.");}},typeof e.on=="function")e.on("load",function(){o=!0});else e.load(function(){o=!0});r.fn=r.prototype={init:function(t,i,r){var f=n(this);this.url=t;this.qs=i;this.lastError=null;this._={keepAliveData:{},connectingMessageBuffer:new k(this,function(n){f.triggerHandler(u.onReceived,[n])}),lastMessageAt:(new Date).getTime(),lastActiveAt:(new Date).getTime(),beatInterval:5e3,beatHandle:null,totalTransportConnectTimeout:0,redirectQs:null};typeof r=="boolean"&&(this.logging=r)},_parseResponse:function(n){var t=this;return n?typeof n=="string"?t.json.parse(n):n:n},_originalJson:t.JSON,json:t.JSON,isCrossDomain:function(i,r){var u;return(i=n.trim(i),r=r||t.location,i.indexOf("http")!==0)?!1:(u=t.document.createElement("a"),u.href=i,u.protocol+a(u.protocol,u.host)!==r.protocol+a(r.protocol,r.host))},ajaxDataType:"text",contentType:"application/json; charset=UTF-8",logging:!1,state:r.connectionState.disconnected,clientProtocol:"2.1",supportedProtocols:["1.5","2.0","2.1"],negotiateRedirectSupportedProtocols:["2.0","2.1"],reconnectDelay:2e3,transportConnectTimeout:0,disconnectTimeout:3e4,reconnectWindow:3e4,keepAliveWarnAt:2/3,start:function(i,h){var a=this,v={pingInterval:3e5,waitForPageLoad:!0,transport:"auto",jsonp:!1},g,y=a._deferral||n.Deferred(),b=t.document.createElement("a"),nt=function(i,u){i.url===u&&i.baseUrl||(i.url=u,b.href=i.url,b.protocol&&b.protocol!==":"?(i.protocol=b.protocol,i.host=b.host):(i.protocol=t.document.location.protocol,i.host=b.host||t.document.location.host),i.baseUrl=i.protocol+"//"+i.host,i.wsProtocol=i.protocol==="https:"?"wss://":"ws://",i.url.indexOf("//")===0&&(i.url=t.location.protocol+i.url,i.log("Protocol relative URL detected, normalizing it to '"+i.url+"'.")),i.isCrossDomain(i.url)&&(i.log("Auto detected cross domain url."),v.transport==="auto"&&(v.transport=["webSockets","serverSentEvents","longPolling"]),typeof i.withCredentials=="undefined"&&(i.withCredentials=!0),n.support.cors||(i.ajaxDataType="jsonp",i.log("Using jsonp because this browser doesn't support CORS.")),i.contentType=r._.defaultContentType))},d,k;if(a.lastError=null,a._deferral=y,!a.json)throw new Error("SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.");if(n.type(i)==="function"?h=i:n.type(i)==="object"&&(n.extend(v,i),n.type(v.callback)==="function"&&(h=v.callback)),v.transport=w(v.transport,a),!v.transport)throw new Error("SignalR: Invalid transport(s) specified, aborting start.");return(a._.config=v,!o&&v.waitForPageLoad===!0)?(a._.deferredStartHandler=function(){a.start(i,h)},e.bind("load",a._.deferredStartHandler),y.promise()):a.state===r.connectionState.connecting?y.promise():s(a,r.connectionState.disconnected,r.connectionState.connecting)===!1?(y.resolve(a),y.promise()):(p(a),v.transport==="auto"&&v.jsonp===!0&&(v.transport="longPolling"),a.withCredentials=v.withCredentials,a._originalUrl=a.url,a.ajaxDataType=v.jsonp?"jsonp":"text",nt(a,a.url),n(a).bind(u.onStart,function(){n.type(h)==="function"&&h.call(a);y.resolve(a)}),a._.initHandler=r.transports._logic.initHandler(a),g=function(i,o){var c=r._.error(f.noTransportOnInit);if(o=o||0,o>=i.length){o===0?a.log("No transports supported by the server were selected."):o===1?a.log("No fallback transports were selected."):a.log("Fallback transports exhausted.");n(a).triggerHandler(u.onError,[c]);y.reject(c);a.stop();return}if(a.state!==r.connectionState.disconnected){var p=i[o],h=r.transports[p],v=function(){g(i,o+1)};a.transport=h;try{a._.initHandler.start(h,function(){var f=r._.firefoxMajorVersion(t.navigator.userAgent)>=11,i=!0;a.log("The start request succeeded. Transitioning to the connected state.");l(a)&&r.transports._logic.monitorKeepAlive(a);a._.keepAliveData.activated&&r.transports._logic.startHeartbeat(a);r._.configurePingInterval(a);s(a,r.connectionState.connecting,r.connectionState.connected)||a.log("WARNING! The connection was not in the connecting state.");a._.connectingMessageBuffer.drain();n(a).triggerHandler(u.onStart);e.bind("unload",function(){a.log("Window unloading, stopping the connection.");a.stop(i)});f&&e.bind("beforeunload",function(){t.setTimeout(function(){a.stop(i)},0)})},v)}catch(w){a.log(h.name+" transport threw '"+w.message+"' when attempting to start.");v()}}},d=a.url+"/negotiate",k=function(t,i){var e=r._.error(f.errorOnNegotiate,t,i._.negotiateRequest);n(i).triggerHandler(u.onError,e);y.reject(e);i.stop()},n(a).triggerHandler(u.onStarting),d=r.transports._logic.prepareQueryString(a,d),a.log("Negotiating with '"+d+"'."),a._.negotiateRequest=function(){var t,h=0,w=100,i,e,o=[],s=[],l=function(n,t){var u=r.transports._logic.prepareQueryString(n,n.url+"/negotiate"),i;return n.log("Negotiating with '"+u+"'."),i={url:u,error:function(t,i){i!==c?k(t,n):y.reject(r._.error(f.stoppedWhileNegotiating,null,n._.negotiateRequest))},success:t},n.accessToken&&(i.headers={Authorization:"Bearer "+n.accessToken}),r.transports._logic.ajax(n,i)},p=function(c){try{t=a._parseResponse(c)}catch(d){k(r._.error(f.errorParsingNegotiateResponse,d),a);return}if(t.availableTransports){e=r._.error(f.aspnetCoreSignalrServer);n(a).triggerHandler(u.onError,[e]);y.reject(e);return}if(!t.ProtocolVersion||a.supportedProtocols.indexOf(t.ProtocolVersion)===-1){e=r._.error(r._.format(f.protocolIncompatible,a.clientProtocol,t.ProtocolVersion));n(a).triggerHandler(u.onError,[e]);y.reject(e);return}if(a.negotiateRedirectSupportedProtocols.indexOf(t.ProtocolVersion)!==-1){if(t.Error){e=r._.error(r._.format(f.errorFromServer,t.Error));n(a).triggerHandler(u.onError,[e]);y.reject(e);return}if(t.RedirectUrl){if(h===w){k(r._.error(f.errorRedirectionExceedsLimit),a);return}v.transport==="auto"&&(v.transport=["webSockets","serverSentEvents","longPolling"]);a.log("Received redirect to: "+t.RedirectUrl);a.accessToken=t.AccessToken;var b=t.RedirectUrl.split("?",2);if(nt(a,b[0]),a._.redirectQs=b.length===2?b[1]:null,a.ajaxDataType==="jsonp"&&a.accessToken){k(r._.error(f.jsonpNotSupportedWithAccessToken),a);return}h++;l(a,p);return}}i=a._.keepAliveData;a.appRelativeUrl=t.Url;a.id=t.ConnectionId;a.token=t.ConnectionToken;a.webSocketServerUrl=t.WebSocketServerUrl;a._.pollTimeout=t.ConnectionTimeout*1e3+1e4;a.disconnectTimeout=t.DisconnectTimeout*1e3;a._.totalTransportConnectTimeout=a.transportConnectTimeout+t.TransportConnectTimeout*1e3;t.KeepAliveTimeout?(i.activated=!0,i.timeout=t.KeepAliveTimeout*1e3,i.timeoutWarning=i.timeout*a.keepAliveWarnAt,a._.beatInterval=(i.timeout-i.timeoutWarning)/3):i.activated=!1;a.reconnectWindow=a.disconnectTimeout+(i.timeout||0);n.each(r.transports,function(n){if(n.indexOf("_")===0||n==="webSockets"&&!t.TryWebSockets)return!0;s.push(n)});n.isArray(v.transport)?n.each(v.transport,function(t,i){n.inArray(i,s)>=0&&o.push(i)}):v.transport==="auto"?o=s:n.inArray(v.transport,s)>=0&&o.push(v.transport);g(o)};return l(a,p)}(),y.promise())},starting:function(t){var i=this;return n(i).bind(u.onStarting,function(){t.call(i)}),i},send:function(n){var t=this;if(t.state===r.connectionState.disconnected)throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");if(t.state===r.connectionState.connecting)throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");return t.transport.send(t,n),t},received:function(t){var i=this;return n(i).bind(u.onReceived,function(n,r){t.call(i,r)}),i},stateChanged:function(t){var i=this;return n(i).bind(u.onStateChanged,function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind(u.onError,function(n,r,u){i.lastError=r;t.call(i,r,u)}),i},disconnected:function(t){var i=this;return n(i).bind(u.onDisconnect,function(){t.call(i)}),i},connectionSlow:function(t){var i=this;return n(i).bind(u.onConnectionSlow,function(){t.call(i)}),i},reconnecting:function(t){var i=this;return n(i).bind(u.onReconnecting,function(){t.call(i)}),i},reconnected:function(t){var i=this;return n(i).bind(u.onReconnect,function(){t.call(i)}),i},stop:function(i,h){var a=this,v=a._deferral;if(a._.deferredStartHandler&&e.unbind("load",a._.deferredStartHandler),delete a._.config,delete a._.deferredStartHandler,!o&&(!a._.config||a._.config.waitForPageLoad===!0)){a.log("Stopping connection prior to negotiate.");v&&v.reject(r._.error(f.stoppedWhileLoading));return}if(a.state!==r.connectionState.disconnected)return a.log("Stopping connection."),t.clearTimeout(a._.beatHandle),t.clearInterval(a._.pingIntervalId),a.transport&&(a.transport.stop(a),h!==!1&&a.transport.abort(a,i),l(a)&&r.transports._logic.stopMonitoringKeepAlive(a),a.transport=null),a._.negotiateRequest&&(a._.negotiateRequest.abort(c),delete a._.negotiateRequest),a._.initHandler&&a._.initHandler.stop(),delete a._deferral,delete a.messageId,delete a.groupsToken,delete a.id,delete a._.pingIntervalId,delete a._.lastMessageAt,delete a._.lastActiveAt,a._.connectingMessageBuffer.clear(),n(a).unbind(u.onStart),delete a.accessToken,delete a.protocol,delete a.host,delete a.baseUrl,delete a.wsProtocol,delete a.contentType,a.url=a._originalUrl,a._.redirectQs=null,s(a,a.state,r.connectionState.disconnected),n(a).triggerHandler(u.onDisconnect),a},log:function(n){v(n,this.logging)}};r.fn.init.prototype=r.fn;r.noConflict=function(){return n.connection===r&&(n.connection=h),r};n.connection&&(h=n.connection);n.connection=n.signalR=r})(window.jQuery,window),function(n,t,i){function s(n){n._.keepAliveData.monitoring&&l(n);u.markActive(n)&&(n._.beatHandle=t.setTimeout(function(){s(n)},n._.beatInterval))}function l(t){var i=t._.keepAliveData,u;t.state===r.connectionState.connected&&(u=(new Date).getTime()-t._.lastMessageAt,u>=i.timeout?(t.log("Keep alive timed out. Notifying transport that connection has been lost."),t.transport.lostConnection(t)):u>=i.timeoutWarning?i.userNotified||(t.log("Keep alive has been missed, connection may be dead/slow."),n(t).triggerHandler(f.onConnectionSlow),i.userNotified=!0):i.userNotified=!1)}function e(n,t){var i=n.url+t;return n.transport&&(i+="?transport="+n.transport.name),u.prepareQueryString(n,i)}function h(n){this.connection=n;this.startRequested=!1;this.startCompleted=!1;this.connectionStopped=!1}var r=n.signalR,f=n.signalR.events,c=n.signalR.changeState,o="__Start Aborted__",u;r.transports={};h.prototype={start:function(n,r,u){var f=this,e=f.connection,o=!1;if(f.startRequested||f.connectionStopped){e.log("WARNING! "+n.name+" transport cannot be started. Initialization ongoing or completed.");return}e.log(n.name+" transport starting.");n.start(e,function(){o||f.initReceived(n,r)},function(t){return o||(o=!0,f.transportFailed(n,t,u)),!f.startCompleted||f.connectionStopped});f.transportTimeoutHandle=t.setTimeout(function(){o||(o=!0,e.log(n.name+" transport timed out when trying to connect."),f.transportFailed(n,i,u))},e._.totalTransportConnectTimeout)},stop:function(){this.connectionStopped=!0;t.clearTimeout(this.transportTimeoutHandle);r.transports._logic.tryAbortStartRequest(this.connection)},initReceived:function(n,i){var u=this,f=u.connection;if(u.startRequested){f.log("WARNING! The client received multiple init messages.");return}u.connectionStopped||(u.startRequested=!0,t.clearTimeout(u.transportTimeoutHandle),f.log(n.name+" transport connected. Initiating start request."),r.transports._logic.ajaxStart(f,function(){u.startCompleted=!0;i()}))},transportFailed:function(i,u,e){var o=this.connection,h=o._deferral,s;this.connectionStopped||(t.clearTimeout(this.transportTimeoutHandle),this.startRequested?this.startCompleted||(s=r._.error(r.resources.errorDuringStartRequest,u),o.log(i.name+" transport failed during the start request. Stopping the connection."),n(o).triggerHandler(f.onError,[s]),h&&h.reject(s),o.stop()):(i.stop(o),o.log(i.name+" transport failed to connect. Attempting to fall back."),e()))}};u=r.transports._logic={ajax:function(t,i){return n.ajax(n.extend(!0,{},n.signalR.ajaxDefaults,{type:"GET",data:{},xhrFields:{withCredentials:t.withCredentials},contentType:t.contentType,dataType:t.ajaxDataType},i))},pingServer:function(t){var e,f,i=n.Deferred();return t.transport?(e=t.url+"/ping",e=u.addQs(e,t.qs),f=u.ajax(t,{url:e,headers:t.accessToken?{Authorization:"Bearer "+t.accessToken}:{},success:function(n){var u;try{u=t._parseResponse(n)}catch(e){i.reject(r._.transportError(r.resources.pingServerFailedParse,t.transport,e,f));t.stop();return}u.Response==="pong"?i.resolve():i.reject(r._.transportError(r._.format(r.resources.pingServerFailedInvalidResponse,n),t.transport,null,f))},error:function(n){n.status===401||n.status===403?(i.reject(r._.transportError(r._.format(r.resources.pingServerFailedStatusCode,n.status),t.transport,n,f)),t.stop()):i.reject(r._.transportError(r.resources.pingServerFailed,t.transport,n,f))}})):i.reject(r._.transportError(r.resources.noConnectionTransport,t.transport)),i.promise()},prepareQueryString:function(n,i){var r;return r=u.addQs(i,"clientProtocol="+n.clientProtocol),r=typeof n._.redirectQs=="string"?u.addQs(r,n._.redirectQs):u.addQs(r,n.qs),n.token&&(r+="&connectionToken="+t.encodeURIComponent(n.token)),n.data&&(r+="&connectionData="+t.encodeURIComponent(n.data)),r},addQs:function(t,i){var r=t.indexOf("?")!==-1?"&":"?",u;if(!i)return t;if(typeof i=="object")return t+r+n.param(i);if(typeof i=="string")return u=i.charAt(0),(u==="?"||u==="&")&&(r=""),t+r+i;throw new Error("Query string property must be either a string or object.");},getUrl:function(n,i,r,f,e){var h=i==="webSockets"?"":n.baseUrl,o=h+n.appRelativeUrl,s="transport="+i;return!e&&n.groupsToken&&(s+="&groupsToken="+t.encodeURIComponent(n.groupsToken)),r?(o+=f?"/poll":"/reconnect",!e&&n.messageId&&(s+="&messageId="+t.encodeURIComponent(n.messageId))):o+="/connect",o+="?"+s,o=u.prepareQueryString(n,o),n.transport&&n.accessToken&&(n.transport.name==="serverSentEvents"||n.transport.name==="webSockets")&&(o+="&access_token="+t.encodeURIComponent(n.accessToken)),e||(o+="&tid="+Math.floor(Math.random()*11)),o},maximizePersistentResponse:function(n){return{MessageId:n.C,Messages:n.M,Initialized:typeof n.S!="undefined"?!0:!1,ShouldReconnect:typeof n.T!="undefined"?!0:!1,LongPollDelay:n.L,GroupsToken:n.G,Error:n.E}},updateGroups:function(n,t){t&&(n.groupsToken=t)},stringifySend:function(n,t){return typeof t=="string"||typeof t=="undefined"||t===null?t:n.json.stringify(t)},ajaxSend:function(t,i){var h=u.stringifySend(t,i),c=e(t,"/send"),o,s=function(t,u){n(u).triggerHandler(f.onError,[r._.transportError(r.resources.sendFailed,u.transport,t,o),i])};return o=u.ajax(t,{url:c,type:t.ajaxDataType==="jsonp"?"GET":"POST",contentType:r._.defaultContentType,headers:t.accessToken?{Authorization:"Bearer "+t.accessToken}:{},data:{data:h},success:function(n){var i;if(n){try{i=t._parseResponse(n)}catch(r){s(r,t);t.stop();return}u.triggerReceived(t,i)}},error:function(n,i){i!=="abort"&&i!=="parsererror"&&s(n,t)}})},ajaxAbort:function(n,i){if(typeof n.transport!="undefined"){i=typeof i=="undefined"?!0:i;var r=e(n,"/abort"),f=n.accessToken?{Authorization:"Bearer "+n.accessToken}:{};t.fetch?t.fetch(r,{method:"POST",keepalive:!0,headers:f,credentials:n.withCredentials===!0?"include":"same-origin"}):u.ajax(n,{url:r,async:i,timeout:1e3,type:"POST",headers:f,dataType:"text"});n.log("Fired ajax abort async = "+i+".")}},ajaxStart:function(t,i){var h=function(n){var i=t._deferral;i&&i.reject(n)},s=function(i){t.log("The start request failed. Stopping the connection.");n(t).triggerHandler(f.onError,[i]);h(i);t.stop()};t._.startRequest=u.ajax(t,{url:e(t,"/start"),headers:t.accessToken?{Authorization:"Bearer "+t.accessToken}:{},success:function(n,u,f){var e;try{e=t._parseResponse(n)}catch(o){s(r._.error(r._.format(r.resources.errorParsingStartResponse,n),o,f));return}e.Response==="started"?i():s(r._.error(r._.format(r.resources.invalidStartResponse,n),null,f))},error:function(n,i,u){i!==o?s(r._.error(r.resources.errorDuringStartRequest,u,n)):(t.log("The start request aborted because connection.stop() was called."),h(r._.error(r.resources.stoppedDuringStartRequest,null,n)))}})},tryAbortStartRequest:function(n){n._.startRequest&&(n._.startRequest.abort(o),delete n._.startRequest)},tryInitialize:function(n,t,i){t.Initialized&&i?i():t.Initialized&&n.log("WARNING! The client received an init message after reconnecting.")},triggerReceived:function(t,i){t._.connectingMessageBuffer.tryBuffer(i)||n(t).triggerHandler(f.onReceived,[i])},processMessages:function(t,i,f){var e;if(i&&typeof i.I!="undefined"){u.triggerReceived(t,i);return}if(u.markLastMessage(t),i){if(e=u.maximizePersistentResponse(i),e.Error){t.log("Received an error message from the server: "+i.E);n(t).triggerHandler(r.events.onError,[r._.error(i.E,"ServerError")]);t.stop(!1,!1);return}u.updateGroups(t,e.GroupsToken);e.MessageId&&(t.messageId=e.MessageId);e.Messages&&(n.each(e.Messages,function(n,i){u.triggerReceived(t,i)}),u.tryInitialize(t,e,f))}},monitorKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring?t.log("Tried to monitor keep alive but it's already being monitored."):(i.monitoring=!0,u.markLastMessage(t),t._.keepAliveData.reconnectKeepAliveUpdate=function(){u.markLastMessage(t)},n(t).bind(f.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t.log("Now monitoring keep alive with a warning timeout of "+i.timeoutWarning+", keep alive timeout of "+i.timeout+" and disconnecting timeout of "+t.disconnectTimeout))},stopMonitoringKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring&&(i.monitoring=!1,n(t).unbind(f.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t._.keepAliveData={},t.log("Stopping the monitoring of the keep alive."))},startHeartbeat:function(n){n._.lastActiveAt=(new Date).getTime();s(n)},markLastMessage:function(n){n._.lastMessageAt=(new Date).getTime();n._.lastActiveAt=n._.lastMessageAt},markActive:function(n){return u.verifyLastActive(n)?(n._.lastActiveAt=(new Date).getTime(),!0):!1},isConnectedOrReconnecting:function(n){return n.state===r.connectionState.connected||n.state===r.connectionState.reconnecting},ensureReconnectingState:function(t){return c(t,r.connectionState.connected,r.connectionState.reconnecting)===!0&&n(t).triggerHandler(f.onReconnecting),t.state===r.connectionState.reconnecting},clearReconnectTimeout:function(n){n&&n._.reconnectTimeout&&(t.clearTimeout(n._.reconnectTimeout),delete n._.reconnectTimeout)},verifyLastActive:function(t){if(!t._.keepAliveData.activated||(new Date).getTime()-t._.lastActiveAt0&&n--}}}();r.transports.foreverFrame={name:"foreverFrame",supportsKeepAlive:function(){return!0},iframeClearThreshold:50,start:function(n,r,e){if(n.accessToken){e&&(n.log("Forever Frame does not support connections that require a Bearer token to connect, such as the Azure SignalR Service."),e());return}var l=this,s=i.foreverFrame.count+=1,h,o=u(),c=function(){n.log("Forever frame iframe finished loading and is no longer receiving messages.");e&&e()||l.reconnect(n)};if(t.EventSource){e&&(n.log("Forever Frame is not supported by SignalR on browsers with SSE support."),e());return}o.setAttribute("data-signalr-connection-id",n.id);f.prevent();h=i.getUrl(n,this.name);h+="&frameId="+s;t.document.documentElement.appendChild(o);n.log("Binding to iframe's load event.");o.addEventListener?o.addEventListener("load",c,!1):o.attachEvent&&o.attachEvent("onload",c);o.src=h;i.foreverFrame.connections[s]=n;n.frame=o;n.frameId=s;r&&(n.onSuccess=function(){n.log("Iframe transport started.");r()})},reconnect:function(n){var r=this;i.isConnectedOrReconnecting(n)&&i.verifyLastActive(n)&&t.setTimeout(function(){if(i.verifyLastActive(n)&&n.frame&&i.ensureReconnectingState(n)){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;n.log("Updating iframe src to '"+t+"'.");u.src=t}},n.reconnectDelay)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){i.ajaxSend(n,t)},receive:function(t,u){var f,e,o;if(t.json!==t._originalJson&&(u=t._originalJson.stringify(u)),o=t._parseResponse(u),i.processMessages(t,o,t.onSuccess),t.state===n.signalR.connectionState.connected&&(t.frameMessageCount=(t.frameMessageCount||0)+1,t.frameMessageCount>r.transports.foreverFrame.iframeClearThreshold&&(t.frameMessageCount=0,f=t.frame.contentWindow||t.frame.contentDocument,f&&f.document&&f.document.body)))for(e=f.document.body;e.firstChild;)e.removeChild(e.firstChild)},stop:function(n){var r=null;if(f.cancel(),n.frame){if(n.frame.stop)n.frame.stop();else try{r=n.frame.contentWindow||n.frame.contentDocument;r.document&&r.document.execCommand&&r.document.execCommand("Stop")}catch(u){n.log("Error occurred when stopping foreverFrame transport. Message = "+u.message+".")}n.frame.parentNode===t.document.documentElement&&t.document.documentElement.removeChild(n.frame);delete i.foreverFrame.connections[n.frameId];n.frame=null;n.frameId=null;delete n.frame;delete n.frameId;delete n.onSuccess;delete n.frameMessageCount;n.log("Stopping forever frame.")}},abort:function(n,t){i.ajaxAbort(n,t)},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(t){o(t,r.connectionState.reconnecting,r.connectionState.connected)===!0&&n(t).triggerHandler(e.onReconnect)}}}(window.jQuery,window),function(n,t){var r=n.signalR,u=n.signalR.events,e=n.signalR.changeState,f=n.signalR.isDisconnecting,i=r.transports._logic;r.transports.longPolling={name:"longPolling",supportsKeepAlive:function(){return!1},reconnectDelay:3e3,start:function(o,s,h){var a=this,v=function(){v=n.noop;o.log("LongPolling connected.");s?s():o.log("WARNING! The client received an init message after reconnecting.")},y=function(n){return h(n)?(o.log("LongPolling failed to connect."),!0):!1},c=o._,l=0,p=function(i){t.clearTimeout(c.reconnectTimeoutId);c.reconnectTimeoutId=null;e(i,r.connectionState.reconnecting,r.connectionState.connected)===!0&&(i.log("Raising the reconnect event"),n(i).triggerHandler(u.onReconnect))},w=36e5;o.pollXhr&&(o.log("Polling xhr requests already exists, aborting."),o.stop());o.messageId=null;c.reconnectTimeoutId=null;c.pollTimeoutId=t.setTimeout(function(){(function e(s,h){var g=s.messageId,nt=g===null,k=!nt,tt=!h,d=i.getUrl(s,a.name,k,tt,!0),b={};(s.messageId&&(b.messageId=s.messageId),s.groupsToken&&(b.groupsToken=s.groupsToken),f(s)!==!0)&&(o.log("Opening long polling request to '"+d+"'."),s.pollXhr=i.ajax(o,{xhrFields:{onprogress:function(){i.markLastMessage(o)}},url:d,type:"POST",contentType:r._.defaultContentType,data:b,timeout:o._.pollTimeout,headers:o.accessToken?{Authorization:"Bearer "+o.accessToken}:{},success:function(r){var h,w=0,u,a;o.log("Long poll complete.");l=0;try{h=o._parseResponse(r)}catch(b){i.handleParseFailure(s,r,b,y,s.pollXhr);return}(c.reconnectTimeoutId!==null&&p(s),h&&(u=i.maximizePersistentResponse(h)),i.processMessages(s,h,v),u&&n.type(u.LongPollDelay)==="number"&&(w=u.LongPollDelay),f(s)!==!0)&&(a=u&&u.ShouldReconnect,!a||i.ensureReconnectingState(s))&&(w>0?c.pollTimeoutId=t.setTimeout(function(){e(s,a)},w):e(s,a))},error:function(f,h){var v=r._.transportError(r.resources.longPollFailed,o.transport,f,s.pollXhr);if(t.clearTimeout(c.reconnectTimeoutId),c.reconnectTimeoutId=null,h==="abort"){o.log("Aborted xhr request.");return}if(!y(v)){if(l++,o.state!==r.connectionState.reconnecting&&(o.log("An error occurred using longPolling. Status = "+h+". Response = "+f.responseText+"."),n(s).triggerHandler(u.onError,[v])),(o.state===r.connectionState.connected||o.state===r.connectionState.reconnecting)&&!i.verifyLastActive(o))return;if(!i.ensureReconnectingState(s))return;c.pollTimeoutId=t.setTimeout(function(){e(s,!0)},a.reconnectDelay)}}}),k&&h===!0&&(c.reconnectTimeoutId=t.setTimeout(function(){p(s)},Math.min(1e3*(Math.pow(2,l)-1),w))))})(o)},250)},lostConnection:function(n){n.pollXhr&&n.pollXhr.abort("lostConnection")},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){t.clearTimeout(n._.pollTimeoutId);t.clearTimeout(n._.reconnectTimeoutId);delete n._.pollTimeoutId;delete n._.reconnectTimeoutId;n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null,delete n.pollXhr)},abort:function(n,t){i.ajaxAbort(n,t)}}}(window.jQuery,window),function(n){function r(n){return n+s}function c(n,t,i){for(var f=n.length,u=[],r=0;r=0}function i(n,t){return new i.fn.init(n,t)}function t(i,r){var u={qs:null,logging:!1,useDefaultPath:!0};return n.extend(u,r),(!i||u.useDefaultPath)&&(i=(i||"")+"/signalr"),new t.fn.init(i,u)}var o=0,s=".hubProxy",h=n.signalR;i.fn=i.prototype={init:function(n,t){this.state={};this.connection=n;this.hubName=t;this._={callbackMap:{}}},constructor:i,hasSubscriptions:function(){return u(this._.callbackMap)},on:function(t,i,u){var c=this,l=c._.callbackMap,v=!u&&e(i),f,h,s,a;for(u=u||i,u._signalRGuid||(u._signalRGuid=o++),t=t.toLowerCase(),f=l[t],f||(f=[],l[t]=f),s=0;s 7 | /// OWIN启动配置类 8 | /// 9 | public class Startup 10 | { 11 | 12 | /// 13 | /// OWIN配置 14 | /// 15 | /// 16 | public void Configuration(IAppBuilder builder) 17 | { 18 | //实例化http/webapi配置 19 | var httpCconf = new HttpConfiguration(); 20 | //配置webapi路由 21 | ApiRouteConfig(httpCconf.Routes); 22 | 23 | //根据配置启用WebApi支持 24 | builder.UseWebApi(httpCconf); 25 | 26 | //启用SignalR并默认映射到“/signalr”路径上 27 | builder.MapSignalR(); 28 | } 29 | 30 | 31 | /// 32 | /// WebApi路由配置 33 | /// 34 | /// 路由集合 35 | void ApiRouteConfig(HttpRouteCollection routes) 36 | { 37 | //忽略的路径 38 | routes.IgnoreRoute("ignore1", "{resource}.axd/{*pathInfo}"); 39 | 40 | //默认配置 41 | routes.MapHttpRoute("default.", "api/{controller}/{id}", new { id = RouteParameter.Optional }); 42 | 43 | } 44 | 45 | } 46 | 47 | 48 | 49 | } -------------------------------------------------------------------------------- /Demos.src/OwinTest/images/jexus_logo_h32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunekit/jexus/9ef192f8d2fedea385856149b66493e157d2584e/Demos.src/OwinTest/images/jexus_logo_h32.png -------------------------------------------------------------------------------- /Demos.src/OwinTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 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 | -------------------------------------------------------------------------------- /Demos.src/OwinTest/web_socket.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | SignalR 测试 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 请输入: 23 | 24 | 25 | 26 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Jexus License 2 | 3 | Copyright (c) 2024 宇内流云 4 | 5 | 当前仓库的中文本、代码、脚本、手册等所有非二进制文件以MIT协议开源。 6 | 7 | All non-binary files such as Chinese books, code, scripts, manuals, etc. in the current repository are open source under the MIT protocol. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Jexus Web Server 3 | 4 | ![JwsLogo](https://www.jexus.org/images/jexus_logo_h32.png) 5 | 6 | Jexus is a linux web server, It began to be publicly released globally in 2008. 7 | 8 | Jexus which has excellent features such as high performance, high security, and high stability, and directly supports asp.net, asp.net core, php and various self-hosted web applications, with flexible and efficient reverse proxy functions, which can be used to deploy web clusters. Jexus has a certain user group and influence at home and abroad, and is a very good web server software. 9 | 10 | The author of Jexus is LiuBing, Email: j66x@163.com. 11 | 12 | 13 | ----------------------------------------------------- 14 | 15 | Jexus 是“Jexus Web Server”的简称,亦可缩写为“JWS”,汉语读音近似于“杰克瑟斯”,也可读着“杰克斯”。Jexus由刘冰(网名:宇内流云)开发,首次公开发布时间是2008年10月。 16 | 17 | Jexus 是一款运行于Linux环境的具有高安全性、高可靠性的高性能WEB服务器、负载均衡网关服务器和WEB应用程序容器。 18 | 19 | Jexus 除了具备通用WEB服务器所必备的功能外,Jexus还直接支持各类ASP.NET Web应用程序,并以特有的“Web应用程序容器技术”无缝对接ASP.NET Core、Tomcat、GoLang Web、Python Web等各类自托管WEB应用程序,为它们提供一致性的集成管控。Jexus内置了久经实战考验的入侵防御系统,其安全等级远高于其它Web服务器软件。 20 | 21 | Jexus 是一款完全由中国人独立研发(不基于任何第三方源码)的在服务器端具有关键作用的原创性国产基础软件,是各种Web应用程序的门户和基石。Jexus不但支持国际上各种主流的Linux发行版,也支持各类主流的国产操作系统,支持龙芯最新指令集(LoongArch64),支持海光、兆芯、飞腾等基于 x86_64 或 aarch64 指令集架构的国产CPU,支持国密(SM2/3/4等),是真正的“安全可靠、可信可控”的“纯国产”WEB服务器中间件, 完全具备党政机关和重要企事业单位“信创”建设所必备的关键品质。 22 | 23 | **Jexus 性能强劲、功能全面、应用场景广泛、服务便捷周到。** 24 | 25 | *\* 如果你希望拥有一款纯粹的国产 WEB 服务器精品用于替代 Nginx、Apache 等外国产品,不管从安全角度还是从低成本角度看,不管是从性能角度还是从功能角度看,Jexus都无疑是你最好的选择 。* 26 | 27 | Jexus 作者的联系邮箱:j66x@163.com,QQ号:273766940. 28 | 29 | ---- 30 | 31 | 🔗 [Jexus 官方网站](https://www.jexus.org)  [用户手册](docs/manual.md)  [用户手册 v8](docs/manual_v8.md)  [GitHub](https://github.com/yunekit/jexus)  [Gitee](https://gitee.com/yunekit/jexus) 32 | 33 | 37 | -------------------------------------------------------------------------------- /docs/manual.md: -------------------------------------------------------------------------------- 1 | # Jexus 用户手册 2 | 3 | *VERSION: 7.0, Available for Jexus v6.x/7.x* 4 | 5 | **作者:宇内流云(j66x@163.com)** 6 | 7 | ----------------------------------------------------------------------- 8 | 9 | 目 录 10 | 11 | 一、Jexus 概述 [......](#一jexus-概述) 12 | 13 | 二、Jexus 的安装与更新 [......](#二jexus-的安装与更新) 14 | 15 | 三、Jexus 的操作命令 [......](#三jexus-的操作命令) 16 | 17 | 四、Jexus 的全局配置 [......](#四jexus-的全局配置) 18 | 19 | 五、Jexus 的网站配置 [......](#五jexus-的网站配置) 20 | 21 | 六、问 答 [......](#六问-答) 22 | 23 | 七、授权与交流 [......](#七授权与交流) 24 | 25 | ## 一、Jexus 概述 26 | 27 | Jexus 是“Jexus Web Server”的简称,可缩写为“JWS”,中文读音近似于“杰克瑟斯”,也可称着“杰克斯”。 28 | 29 | Jexus是Linux平台上具有高安全性、高可靠性、高稳定性的高性能WEB服务器、反向代理网关服务器和自宿主WEB应用程序的托管(容器)服务器。 30 | 31 | Jexus除了具备通用WEB服务器所必备的静态文件处理和缓存功能外,还直接支持ASP.NET WEB应用程序,支持PHP,支持反向代理。自6.0版开始,Jexus开创性地设计了“AppHost”功能(HTTP应用程序容器),通过此功能,Jexus可以为Asp.Net Core、Tomcat、GoLang Web、Python Web等“自带HTTP功能的WEB应用程序”提供一体化集成管理。 32 | 33 | 就中国而言,Jexus是一款完全由中国人自主研发的具有原创性特质的国产服务器软件,支持国产操作系统和国产CPU芯片,真正做到了“安全、可靠、可信、可控”,具备我国党政机关和重要企事业单位信息化建设所需要的关键品质。 34 | 35 | **Jexus 有如下特点:** 36 | 37 | 1、性能强劲: 38 | 39 | 在操作系统支持方面,Jexus的设计目标非常明确:只考虑支持Linux和国产操作系统。正因为目标明确,定位清晰,针对性极强,所以在设计过程中完全不需要考虑兼容其它操作系统而不得不编写的大量冗余代码,也无需使用低性能的折衷方案,而是直接使用Linux的长项技术,比如epoll技术、sendfile零拷贝技术等等,核心技术直通操作系统。在编程技术方面,Jexus使用了c/c++与c#的混合编程技术,.Net Native技术,从语言层面为Jexus提供了坚实的保证。 40 | 41 | 2、安全可靠: 42 | 43 | 安全性是Jexus有别于其它Web服务器(比如Nginx、Apache、IIS等)的一个重要特色。Jexus初始开发于2006年,2008年在www.linuxdot.net上公开发布1.0版,最初开发的目的是为某政府网站提供Asp.Net跨平台支持,因此,Jexus从诞生开始,就已将其安全性作为开发的首要目标,不但在编码中努力避免漏洞和安全缺陷,而且还开创性地内置了"入侵检测和控制"技术,并在长期的运行实践中不断地完善其策略,优化其算法,为Jexus托管的网站提供了强有力的安全保护。 44 | 45 | 可靠性是Jexus另一个突出优点。可靠性主要表现在两个方面,一是稳定性,二是"可信可控"特性。在稳定性方面,由于Jexus采用了独特的混合编程技术,通过语言层面的扬长避短,在高性能与高可用性方面得到了平衡。另外,10多年来,Jexus在各种生产场景中摸爬滚打,在"实战"环境中不断地发现问题解决问题,不断地完善,保证了Jexus持续可靠的稳定性。在"可信可控"特质方面,Jexus是我国唯一一款由中国人(宇内流云/刘冰先生)从0开始原生开发的国产WEB服务器,整个Jexus程序系统没有使用任何特殊的第三方组件,甚至连开源代码也基本上没有"参考"使用,是纯粹的"中国血统",从程序开发者身份到程序源代码、程序运行环境等方面确保了Jexus"安全可靠""可控可信"的"信创"品质。 46 | 47 | 3、功能强大: 48 | 49 | 首先,Jexus是一款如同Nginx、Apache那样的"通用web服务器",具有通用web服务器必要的功能,除此之外,Jexus还具备如下功能特征: 50 | 51 | (1)支持ASP.NET。在Linux上支持Asp.Net运行,是Jexus的重要的特色功能之一(甚至一度被部分用户误认为Jexus就是一款单纯的Asp.Net服务器)。Jexus直接支持.NET Framework 3.5至4.8范围内的所有版本的Asp.Net Web程序。Jexus v6.0以上版本已经集成.NET运行环境,服务器无需另行安装mono之类的.NET运行时。 52 | 53 | (2)提供Fast-CGI协议支持。通过Fast-CGI,Jexus能支持包括PHP在内的所有拥有Fast-CGI服务接口的WEB应用; 54 | 55 | (3)具有强劲的反向代理功能。Jexus支持全站反向代理、部分路径反向代理,支持多目标服务器反向代理(负载均衡),支持TCP"穿透"技术。Jexus的反向代理配置简洁灵活,性能好,功能强。 56 | 57 | (4)"AppHost"(自宿主WEB应用程序托管容器)是Jexus独创功能,它的作用是将ASP.NET Core或node.js、tomcat等"自宿主(自带HTTP服务)"式的以控制台方式运行的web应用程序集以Jexus子进程的形式纳入Jexus统一管理,而无需进行反向代理设置,也无需采用其它方式对这些目标程序进行运行和守护; 58 | 59 | (5)具备基于正则表达式的强大的"URL重写"功能; 60 | 61 | (6)支持https,具有SSL(TLS v1.0-1.3)加密数据安全传输能力; 62 | 63 | (7)直接内置“**入侵检测**”安全模块,具有入侵检测和防御功能,能自动识别和阻止非法请求。这是Jexus有别于其它Web服务器的重要的特色功能之一; 64 | 65 | (8)直接支持符合OWIN标准的WEB应用程序,如Nancy、SignalR等符合OWIN标准的应用框架,支持WebSocket; 66 | 67 | (9)支持包括龙芯在内的国产CPU和国产操作系统。 68 | 69 | ## 二、Jexus 的安装与更新 70 | 71 | (一)安装前的准备工作: 72 | 73 | 1、GLIBC版本:Jexus 7.0/7.1的编译环境是CentOS7.9,对GLIBC版本的最低要求是2.17,因此,Jexus应该在CentOS 7.9、Ubuntu 16.04以上版本或者满足GLIBC版本要求的国产操作系统上运行。 74 | 75 | 2、依赖库:Jexus使用了.NET NativeAOT技术,集成了Mono环境,因此,安装Jexus的Linux系统需要存在或安装libicu、libkrb5函数库。常用Linux一般默认已经安装了libkrb6或libicu,部分系统需要手工安装。 76 | 77 | 3、管理员身份:Jexus的安装、启动、停止等操作,操作者必须具备服务器管理员身份(即能使用su或sudo等命令提供root权限)。 78 | 79 | (二)在线安装: 80 | 81 | 指的是服务器能够连接互联网,服务器通过互联网从Jexus官方网站获取最新正式版安装包进行自动安装。安装命令是: 82 | 83 | - X86_64(amd64)系统: 84 | 85 | ``` 86 | curl https://jexus.org/release/x64/install.sh | sudo sh 87 | ``` 88 | 89 | - AARCH64(arm64)系统: 90 | 91 | ``` 92 | curl https://jexus.org/release/arm64/install.sh | sudo sh 93 | ``` 94 | 95 | - 龙芯(LoogArch64)系统: 96 | 97 | ``` 98 | curl https://jexus.org/release/la64/install.sh | sudo sh 99 | ``` 100 | 101 | *\* 强调:使用上述命令前应该确定自己当前身份是否是root身份。* 102 | 103 | (三)离线安装: 104 | 105 | 指的是从jexus官方网站下载Jexus安装包进行安装。这种安装方式常常用于服务器无法访问互联网的内网服务器。 106 | 107 | Jexus安装包是一个"tar.gz"压缩包,因此,"离线安装"过程,其实就是下载安装包,传输到目标服务器,解压到工作目录这样的操作过程。 108 | 109 | 1、下载:到www.jexus.org下载你需要的Jexus安装包。 110 | 111 | 2、将安装包上传到服务器的安装目录。Jexus习惯上使用"/usr/jexus"作为工作目录,因此,建议你把安装包上传到"/usr/"文件夹中。 112 | 113 | 3、解压:在jexus安装包所在文件夹中,以root身份解压Jexus压缩包(安装包)。如:sudo tar -zxvf jexus-7.2.x-x64.tar.gz 114 | 115 | 4、删除安装包。 116 | 117 | 5、从备份文件夹中恢复之前Jexus的配置文件(如果有)。 118 | 119 | (四)关于Jexus的更新或重装: 120 | 121 | 由于Jexus是绿色软件,所谓的"安装"其实就是下载和解压,因此,Jexus的更新、重装,其实与上述安装过程是相似的,不同点就是在安装之前多两个步骤: 122 | 123 | (1)停止Jexus运行; 124 | 125 | (2)删除或更名(备份)之前的Jexus的工作文件夹。 126 | 127 | (五)检查版本和初始化: 128 | 129 | 1、检查Jexus版本号的命令是"/usr/jexus/jws -V",如果Jexus安装正常,此命令将打印出Jexus的版本号以及集成的 mono.net 运行时的版本号。 130 | 131 | 2、初始化命令是"sudo /usr/jexus/jws init",这是安装Jexus后的非必要操作,但是建议执行一次。 132 | 133 | 134 | ## 三、Jexus 的操作命令 135 | 136 | (一)基本操作命令: 137 | 138 | Jexus核心命令是"jws",如果你把jexus安装在"习惯"位置,即“/usr/jexus/”文件夹中,那么,含绝对路径的完整命令将是“/usr/jexus/jws”。 139 | 140 | “jws”其实是一个shell脚本文件,具体内容可以自行查看。 141 | 142 | Jexus的操作是通过为"jws"提供不同的操作参数实现的,包括“启动(start)”“停止(stop)”“重启(restart)”“运行状态(status)”“初始化(init)”“显示版本(-V)”。初始化和显示版本号已经在前文进行了说明,不再此处复述。 143 | 144 | 1、启动命令 145 | 146 | (1)启动Jexus程序并同时启动所有的网站: 147 | 148 | sudo /usr/jexus/jws start 149 | 150 | (2)在Jexus运行期间单独启动一个网站: 151 | 152 | sudo /usr/jexus/jws start 网站名 153 | 154 | *\* 注:"网站名"在Jexus中,指的是网站配置文件名,jexus的网站配置文件即是"网站名"。* 155 | 156 | 2、停止命令 157 | 158 | (1)停止Jexus程序同时停止所有的网站: 159 | 160 | sudo /usr/jexus/stop 161 | 162 | (2)停止一个指定的网站(web应用程序) 163 | 164 | sudo /usr/jexus/stop 网站名 165 | 166 | 3、重启命令 167 | 168 | (1)重启jexus程序和所有的网站程序 169 | 170 | sudo /usr/jexus/jws restart 171 | 172 | (2)重启指定的网站程序 173 | 174 | sudo /usr/jexus/jws restart 网站名 175 | 176 | 4、显示Jexus是否处理于运行状态 177 | 178 | sudo /usr/jexus/jws status 179 | 180 | 注意:以上命令都加有sudo命令,指的是以管理员身份发起操作,如果你本来就是管理员身份,就不必使用sudo。 181 | 182 | (二)关于开机启动: 183 | 184 | 让Jexus实现开机启动,有两个办法(强调:只能选其中之一): 185 | 186 | **方法一**:通过“/etc/rc.local”进行。 187 | 188 | 具体方法是:在“/etc/rc.local”文件中加入“/usr/jexus/jws start”一行。 189 | 190 | 此方法使用简便,但要注意:一是要把jws的路径和文件名写正确,要使用“jws”命令文件的绝对路径;二是rc.local文件要有"可执行"属性,建议运行一下这个命令:“sudo chmod +x /etc/rc.local”。 191 | 192 | **方法二**:将jexus注册到systemd中成为系统服务。 193 | 194 | 具体方法如下: 195 | 196 | 1、注册服务:sudo systemctl enable /usr/jexus/jws.service 197 | 198 | 2、启动服务:sudo systemctl start jws 199 | 200 | 3、停止服务:sudo systemctl stop jws 201 | 202 | 4、重启服务:sudo systemctl restart jws 203 | 204 | 5、注销服务:sudo systemctl disable jws 205 | 206 | *\*\*\* 特别强调:一旦jexus被注册为系统服务之后,jexus的启动、停止、重启等操作只能使用systemctl命令进行操作,不能再使用不带网站参数的“jws start”“jws stop”“jws restart”等命令进行操作。* 207 | 208 | 209 | ## 四、Jexus 的全局配置 210 | 211 | 在jexus的工作文件夹中(一般是"/usr/jexus")有一个基本的配置文件,文件名是"jws.conf",这是Jexus的全局配置文件。 212 | 213 | 主要配置项如下: 214 | 215 | SiteConfigDir:指定网站配置文件夹的路径,默认配置是jexus程序文件夹下的"siteconf"子目录。该项配置不建议修改。 216 | 217 | SiteLogDir:指定jexus日志和网站访问日志的存放路径。默认配置是jexus程序文件夹下的"log"子目录。该项配置不建议修改。 218 | 219 | Httpd.Processes:指的是jexus httpd工作进程的数量,默认为2,设为0表示自动,即由jexus根据服务器cpu数量自行确定。 220 | 221 | 该项配置能影响Jexus处理html等静态文件以及数据转发等方面的性能,其具体数量应该结合服务器cpu数量、内存富余度和并发请求数量综合考虑,一般来情况下配置2个进程就行,如果服务器并发流量大,可以按cpu核心数的三分之一或二分之一设置。 222 | 223 | Httpd.MaxTotalMemory:指的是允许Jexus的httpd工作进程最多使用多少内存,单为是"兆字节",设为0表示"自动"。 224 | 225 | Httpd.MaxCpuTime:表示Jexus的httpd工作进程最多能使用多少cpu时间,单位为秒,达到这个时间值后,jexus的httpd工作进程就会自动重启。设为0表示禁用这个选项。 226 | 227 | Httpd.MaxConnPerIp:表示jexus httpd工作进程允许每个IP地址能同时发出多少个TCP连接,当某IP地址超过这个数量的连接时,Jexus将拒绝接受。设为0表示禁用这个选项。 228 | 229 | Php-fcgi.Set:PHP运行环境设置,本设置的值分两部分,两个部分之间用英文逗号分开。第一部分是指定php-cgi命令文件的完整路径(一般是"/usr/bin/php-cgi");第二部分是设定php-cgi的最大工作进程数量。本项设置不是必须的,如果你服务器不通过Jexus直接运行PHP WEB程序,就可以禁用该项(在配置行前加"#"号)。 230 | 231 | ## 五、Jexus 的网站配置 232 | 233 | Jexus Web Server 可以同时运行多个站点,没有数量限制。 234 | 235 | 一个具体的站点,由"端口""域名""虚拟根路径"三个基本要素确定,三个要素中任何一项不同,即表示不同的站点。 236 | 237 | 必须把所有网站配置文件放到全局配置文件"jws.conf"所指定的网站配置文件夹内(默认是jexus工作目录内的"siteconf"子文件夹)。这个文件夹除了网站配置文件,不能有其它任何文件,因为jexus会认为该文件夹中的任何一个文件都代表着一个具体的网站配置(子文件夹除外,不在jexus搜索之列)。 238 | 239 | 每个网站有且只有一个配置文件,配置文件的文件名代表这个网站的名称,因此配置文件的文件名应该尽量具有网站个性特点,以便管理员容易记忆和识别。要特别注意:文件名不能有空格!支持中文,但不建议使用。 240 | 241 | 一个网站可以拥有任意多的域名或"泛域名",不同网站不能有相同的域名,每个端口最多只能有一个没有域名的网站(域名栏填"\*"号)。在80端口,这个没有域名的网站叫做"默认网站"。 242 | 243 | (一)网站配置文件的基本配置项: 244 | 245 | port:站点服务端口,这是必填项。http的标准端口是80,https的标准端口是443,最大值是65535。 246 | 247 | root:根路径。指定网站虚拟根路径(URL根路径)和存放网站文件的物理文件夹路径(物理根),两种路径用一个以上英文空格符分开。URL根路径常常是“/”。如果你的网站文件存放在“/var/www/default”这个文件夹中,那么,root项就应该是“root=/ /var/www/default”。 248 | 249 | **root 项是必填项**。如果遇上在当前服务器并没有网站程序的"全站反向代理"这种情况,你可以在服务器单独创建一个空白文件夹并设定它,也可以设为"/tmp"。 250 | 251 | hosts:配置网站的域名。一个站可以指定多个域名,各域名间用英文逗号分隔。也可以配置“泛域名”代指某个域名下的所有子域名,比如“\*.mysite.com”。如果填“\*”号,就表示访问这个站可以是任意的域名或是服务器IP地址,即,区分这个网站不是用域名区分,而是用端口或虚拟根路径进行区分。 252 | 253 | (二)网站配置文件的拓展(高级)配置 254 | 255 | 一个网站配置了前述的三项基本选项一般就能正常运行了。本部分指的是基本配置之外的拓展(高级)配置,包括"反向代理""URL重写""端口转发""AppHost""IP地址黑(白)名单"等等。 256 | 257 | 具体说明如下: 258 | 259 | **1、默认首页(Indexes):** 260 | 261 | Jexus已经内置默认首页的定义,包括index.html、index.htm、default.html、default.htm、index.aspx、default.aspx。如果你网站的首页没有被包括在这些默认首页中,可以通过启用该选项指定你的默认首页。 262 | 263 | **2、URL重写(rewrite):** 264 | 265 | "URL重写"是指WEB服务器将访问者向服务器发起的URL请求按指定的匹配规则解释和匹配到另外的一个真实RUL路径资源上。比如,当访问者访问".php"、".asp"等类型的文件时,服务器以 "/404.html" 这个URL应答,你可以设为: 266 | 267 | rewrite=^/.+?\\.(asp|php|cgi)$ /404.html 268 | 269 | rewrite格式: 270 | 271 | "rewrite="的后面是两部分阻成,两部分之间由一个空格分开。 272 | 273 | 空格前是匹配的条件:用正则表达式描述URL的匹配条件。 274 | 275 | 空格后是匹配的目标:指的是如果用户访问的路径合乎前面的匹配条件时,服务器将以哪个规则回应。 276 | 277 | 如: 278 | 279 | 把"/bbs"匹配到"/bbs/index.aspx",把"/bbs/file-编号" 匹配到"/bbs/show.aspx?id=编号": 280 | 281 | ``` 282 | rewrite=^/bbs$ /bbs/index.aspx 283 | rewrite=^/bbs/file-([0-9]{1,6})$ /bbs/show.aspx?id=$1 284 | ``` 285 | 286 | 格式解释:rewrite的等号后含有两部分内容,用空隔分开。前半部分是一个正则表达式,用于描述需要URL重写的(用户浏览器中的)url路径样式,后半部分是当用户的URL合乎前面的正则表达式时,JWS应该重写和访问的真实URL路径。 287 | 288 | **3、禁止或允许某IP或IP段访问网站(denyfrom、allowfrom)**: 289 | 290 | A、只允许某些IP地址访问网站(白名单功能) 291 | 292 | 默认情况下,允许所有IP地址访问。如果手工设置IP地址白名单,那么,白名单之外的IP地址会自动划入黑名单而禁止访问服务器。 293 | 294 | 配置格式,形如: 295 | 296 | allowfrom=1.2.3.\*   \# 一个IP地址段 297 |
298 | AllowFrom=2.2.3.3   \# 一个具体的IPv4地址 299 | 300 | B、禁止某IP或某IP段访问网站(黑名单功能) 301 | 302 | 默认情况下,本配置为空。如果手工添加需要禁止访问的IP地址(段),必须合乎一个规则:黑名单必须是白名单的真子集。 303 | 304 | 配置格式,形如: 305 | 306 | denyfrom=111.222.111.\*   \# 一个IP地址段 307 |
308 | denyfrom=101.201.1.132   \# 一个IP地址 309 | 310 | *注:为了不使配置复杂化,建议只使用"黑名单"。* 311 | 312 | **4、禁止访问某文件夹及其子文件夹中的内容(DenyDirs):** 313 | 314 | 格式是:DenyDirs=PATH 315 | 316 | PATH指的是相对于网站根文件夹的URL路径,如 "/abcfiles"或"~/abcfiles"。可以同时指定多个路径,用英文逗号分开。 317 | 318 | **5、是否对请求的URL参数等进行安全检测(CheckQuery)**: 319 | 320 | 本选项的值包括true和false之一,默认是true,即需要进行安全检查,除非你的确需要关掉这个选项,否则可以不填。格式如下: 321 | 322 | CheckQuery=false 323 | 324 | *注:关掉本项可以提高服务器速度,但就安全而言,不建议关掉它。* 325 | 326 | **6、NOFILE(无文件替代)功能:** 327 | 328 | 本功能是Jexus的特有功能,指的是如果服务器找不到真实文件时,服务器将用使用一个什么文件去替代。格式是: 329 | 330 | NoFile=PATH 331 | 332 | PATH:指定的替代文件和相当于网站根文件夹的完整路径,如: 333 | 334 | Nofile=/mvc/controller.aspx或者Nofile=/index.php 335 | 336 | (提示:替换后,Jexus会把原始请求的路径资源保存到一个名为"X-Real-Uri"的HTTP请求头中,以便目标文件作进一步处理) 337 | 338 | **7、关闭网站访问日志(NoLog)**: 339 | 340 | nolog=yes 341 | 342 | (注:禁用网站日志功能会轻微提高WEB服务器系统的处理速度,但不足也是明显的,就是你无法详细了解网站的访问情况了) 343 | 344 | **8、长连接开关(Keep_Alive)**: 345 | 346 | Keep_Alive=true或false 347 | 348 | 注:其值为true、false或yes、no,默认使用长连接(true),大多数情况可以不使用该选项,除非你认为真有必要禁止长连接。 349 | 350 | **9、反向代理功能(Reproxy):** 351 | 352 | 这是Jexus的一个重要功能。指的是当用户访问某个路径或任何路径时,Jexus将这个请求转发给其它的服务器。 353 | 354 | 通过Reproxy功能,我们可以实现负载均衡或"高可用性"。 355 | 356 | 格式是: 357 | 358 | Reproxy=请求路径 目标服务器IP:端口,如: 359 | 360 | Reproxy=/ 1.1.1.1:800  (全站反代) 361 |
362 | Reproxy=/abc 1.1.1.1:900/abc  (部分路径反代) 363 |
364 | Reproxy=/ 1.1.1.1:5000, 2.2.2.2:5000  (多目标服务器反代) 365 | 366 | 值得强调的是,"多目标服务器反向代理"将产生负载均衡(WEB集群)和高可用性效果,即,当用户访问该站时,Jexus就会随机选择一台后端服务器进行处理,如果Jexus发现某台服务器已经宕机而没有应答,Jexus就会把请求转发给其它剩余的服务器。 367 | 368 | 注意:当"部分反代"和"全站反代"多条规则同时出现时,一定要把"全站反代"那一条规则放到最后。 369 | 370 | **10、透明反代(TCP透传):** 371 | 372 | 这是Jexus 7.1开始支持的技术,具体操作办法就是在反向代理设置项中,加上“Transparent”字串参数。更多相关信息请参见本手册“问答”部分。 373 | 374 | **11、使用FAST-CGI提供的服务(FastCGI.Add):** 375 | 376 | 对于TCP连接的fast-cgi: 377 | 378 | fastcgi.add=php | tcp:127.0.0.1:9000 379 | 380 | 对于unix sockets连接的FastCGI: 381 | 382 | fastcgi.add=php,php3 | socket:/tmp/phpsvr 383 | 384 | **12、启用或关闭gzip压缩功能(UseGZIP):** 385 | 386 | UseGzip=true或false 387 | 388 | 解释:Jexus默认已经启用gzip压缩功能,所以,除非你希望禁止该功能,才需要显式地设置此选项。 389 | 390 | **13、指定网站的用户身份(User):** 391 | 392 | 格式是:User=用户名 393 | 394 | "用户名"指的是Linux系统中已经存在的一个用户名称,比如 www-data。 395 | 396 | 默认情况下,jexus将使用"root"作为网站工作进程的用户身份,这种身份的权限很高,为了网站更加安全,建议指定一个权限低的用户身份,而"www-data"是大多数Linux系统的内置的专为网站准备的一个用户名,其权限较低,适合指定给网站使用。 397 | 398 | **14、启用HTTPS进行SSL安全传输(UseHttps):** 399 | 400 | 要为网站启用https,首先需设置UseHttps=ture选项,表示该站启用Https安全连接。另外,对于https,网站端口应该设为443(https默认端口)。 401 | 402 | 然后是设置SSL证书、私钥、协议版本、加密套件。 403 | 404 | (1)设置证书文件: 405 | 406 | ssl.certificate=完整的证书路径和文件名 407 | 408 | (2)设置密钥文件: 409 | 410 | ssl.certificatekey=密钥文件的完整路径和文件名 411 | 412 | (3)协议版本: 413 | 414 | ssl.protocol=版本列表 415 | 416 | 指的是ssl的版本号,包括:TLSv1.0、TLSv1.1、TLSv1.2和TLSv1.3。可以同时多个版本号,各版本号之间用空格分隔。 417 | 418 | 该选项不是必填项,但为了安全起见,建议启用并选择TLSv1.1、TLSv1.2、1.3三个版本号。 419 | 420 | (4)加密套件: 421 | 422 | ssl.ciphers=ECDHE-RSA-AES256-GCM-SHA384:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE 423 | 424 | 本选项不是必填项。 425 | 426 | **15、ASP.NET网站专用配置:** 427 | 428 | (1)配置Asp.Net工作进程数量(默认为1): 429 | 430 | 格式为:AspNet.Workers=工作进程数 431 | 432 | 默认情况下,Jexus为Asp.Net提供1个工作进程,单工作进程的好处是节约内存,但也有弱点,比如,难以充分发挥多CPU(多核)的性能优势,大并发承受力、容灾能力等方面比多工作进程弱等等。 433 | 434 | 为Asp.Net开启"多进程"工作模式后,ASP.NET网站的web.config中的Session状态服务不能再使用"InProc"模式,而应该使用"StateServer"等其它非程序内存贮模式,否则极易出现Session数据丢失问题。 435 | 436 | 另外,当Asp.Net使用多进程并行处理时,为了在验证数据的加密、解密方面保证一致性,还应该在web.config文件夹中的"system.web"节中配置"machineKey"项。例如作如下配置: 437 | 438 | ``` 439 | 443 | ``` 444 | 445 | 设置ASP.NET多进程数量并非越多越好,要综合考虑服务器CPU核数、可用内存、并发压力等多方面因素。一般来说,进程数量配置为CPU核数的一半就行了,最多不要超过CPU的核心数。 446 | 447 | (2)限制ASP.NET消耗CPU的时间数(默认为0,无限制): 448 | 449 | 格式是:AspNet.MaxCpuTime=单位为秒的时间值 450 | 451 | 配置该选项后,当该网站使用cpu的时间累积到你指定的最大值之后,Jexus将重启这个网站。 452 | 453 | (3)限制Asp.Net使用内存的最大值(默认为0,无限制): 454 | 455 | 格式是:AspNet.MaxMemory=以兆为单位的内存数 456 | 457 | 设置该选项后,当你的网站程序使用的内存超过指定的最大值时,Jexus将重启该站,释放之前占用的内存空间。 458 | 459 | **16、WEB应用程序端口转发(AppHost.Port):** 460 | 461 | 这是Jexus特有功能。 462 | 463 | 格式是:AppHost.Port=端口号 464 | 465 | 本项配置指的是将当前网站的请求数据全部转发到本机某WEB应用程序侦听端口,比如把请求转发到Asp.net Core应用程序的5000端口上。 466 | 467 | 可以使用多个端口,各端口号之间用英文逗号分开。 468 | 469 | 端口转发与反向代理功能相近,但端口转发的性能更高。 470 | 471 | 注意:一是端口转发不能用于虚拟根路径不是"/"的网站;二是端口转发只能在同一服务器上进行。 472 | 473 | **17、自宿主WEB应用程序托管服务(AppHost):** 474 | 475 | “自宿主web应用程序托管”是Jexus的一项独有的重要功能,它为Asp.Net Core的Kestrel、Java的Tomcat等自带http服务的“自宿主Web应用程序”提供了简单可靠、可控的一体化集成管理。 476 | 477 | 格式:AppHost=\{Cmd=命令及参数; AppRoot=应用程序所在路径; AppPort=应用程序端口号\} 478 | 479 | 也可以分行书写(分行书写时,AppHost和"={"必须在同一行),例如: 480 | 481 | ``` 482 | AppHost={ 483 | Cmd=命令及参数; 484 | AppRoot=应用程序文件夹路径; 485 | AppPort=应用程序端口号; 486 | } 487 | ``` 488 | 子配置项说明: 489 | 490 | Cmd:必选项。表示启动这个web应用程序的命令和命令参数,如 Cmd=dotnet /var/www/mysite/webapp.dll。 491 | 492 | AppRoot:非必选项。表示这个应用程序所在的文件夹路径,如:AppRoot=/var/www/mysite。如果本项没有指定,Jexus将使用网站root项中的根文件夹路径作为应用程序路径。 493 | 494 | AppPort:必填项。表示这个应用程序使用的侦听端口,多个端口用英文逗号分隔。特别强调,AppHost中的port不能与网站的port相同。 495 | 496 | Env:可选参数。表示这个应用程序工作时需要的环境变量,如Env=(PATH=/myhost/bin:$PATH),多个设置用英文逗号分开。 497 | 498 | ErrLog:可选项。表示将这个应用程序的异常输出重定向到指定的文件(需填写完整路径); 499 | 500 | OutLog:可选项。表示将这个应用程序的控制台输出重定向到指定的文件(需填写完整路径); 501 | 502 | User:可选项。以指定的用户身份运行该应用程序,默认为root身份。 503 | 504 | 注意: 505 | 506 | 1、AppHost像AppHost.Port一样,不支持虚拟路径; 507 | 508 | 2、AppHost功能是将指定的具有http服务能力的web应用程序纳入jexus工作进程进行管理,对Asp.Net Core或node.js等自宿主web应用程序用于生产环境时具有重要的意义。 509 | 510 | 3、不要同时启用AppHost和AppHost.Port。 511 | 512 | 513 | **18、Mime配置** 514 | 515 | Jexus中,描述数据类型的配置文件是jexus根文件夹中的mime.conf文件,它是一个文本文件,可以使用vim等任何文本工具进行编辑修改。mime.conf每行代表一个类型的数据,每一行由“文件扩展名”和“数据类型描述”两部分组成,两部分由英文冒号分开。 516 | 517 | ## 六、问 答 518 | 519 | Jexus从2008年正式公开发布以来,一直受到了国内外Web建设者们的关注,涉及到了各种规模和不同类型的项目,通过与开发者们的互动,我们得到比较丰富的使用经验和问题处理经验。本章节我们以问答的形式向大家共享一些具有普遍性的经验或常识。 520 | 521 | **1、如何评估传统的Asp.Net是否能迁移到Linux平台?** 522 | 523 | 一个重要的观察点就是你的Asp.Net应用程序以及所引用的第三方库是否是“标准的”、“通用的”,即是否不依赖Windows系统所特有的功能、组件和系统函数。比如,不能使用Windwos特有的ActivX/COM技术,不能通过Dllmport方法调用windows特有的kernel.dll、user32.dll等函数库的函数。另外,要符合linux的文件与路径命名规则,比如文件名大小写敏感等。 524 | 525 | 526 | **2、传统Asp.Net在Linux平台上性能如何?** 527 | 528 | “传统Asp.Net”指的是有别于“Asp.Net Core”的基于传统的“.NET Framework(.NET 1.0-4.8.x)”运行环境的 .NET WEB应用程序框架。 529 | 传统的ASP.NET程序在Linux上运行是依赖“mono”这个开源的跨平台运行时及其相应的跨平台通用类库(Jexus已集成mono环境,不需要单独安装)。现在,mono在技术上已经成熟,性能也很优秀,但由于它需要照顾“跨平台”这个特殊需求,部分特性必须作出一些折中处理,因此部分性能可能略弱于只能在Windows系统上运行的.NET Framework。 530 | 531 | **3、为什么说Jexus是Asp.Net Core的最理想的宿主服务器?** 532 | 533 | Jexus通过“AppHost”配置支持ASP.NET Core应用程序。“AppHost”是Jexus专门针对那些自身具有http通讯功能的WEB应用程序设计的一项服务功能,该功能相当于一个容器,把HTTP自宿主应用程序作为Jexus的一个子进程纳入Jexus的统一管理、守护和进程间数据转发,ASP.NET Core等HTTP自宿主程序不再需要寻求其它方式运行和守护,具有维护简单,管理方便和数据转发高效稳定的优势。 534 | 535 | *\* 值得一提的是,Jexus不但是ASP.NET Core的理想宿主,也可以是tomcat、node.js等自带HTTP功能的WEB应用程序的理想容器,它们都可以纳入Jexus的AppHost容器进行管控和运行。* 536 | 537 | **4、作反向代理服务器,用Jexus还是用Nginx?** 538 | 539 | Nginx是一款在中国国内使用得比较广泛的一款优秀的反向代理服务器,与Nginx相同的是,Jexus同样也具有灵活高效的反向代理能力,与nginx不同的是,Jexus的反向代理具有安全检测功能,较nginx那种单纯的数据转发,Jexus的反向代理更加安全可靠。 540 | 541 | *\* 因此,从安全角度看,Jexus更适合做反向代理服务器、负载均衡网关服务器,更适合成为大型WEB项目面向公众一侧的“入口服务器”;从国内的环境角度看,Jexus作为一款性价比极高、安全性极强的具有自主知识产权的纯国产Web服务器中间件,更是“信创”建设中Web服务器、反向代理服务器最明智的首选项。* 542 | 543 | **5、迁移Asp.Net应用程序到Linux平台时要注意些什么?** 544 | 545 | (1)编译发布:建议使用预编译发布而不是直接将源码上传到服务器。 546 | 547 | (2)清理web.config: 548 | 549 | 确保你的web.config是使用的UTF8字符集编码,如果不是UTF-8编码,必须去掉所有的中日韩等各种非英文文字。 550 | 551 | 将web.config中的所有“\\”号改为“/”号;将所有的windows路径字串改为符合linux规范的linux路径。 552 | 553 | (3)最小化引用: 554 | 555 | 用Visual Studio的ASP.NET模板创建的默认网站,VS会自动给你添加大量的程序集引用,大量的引用,看似更加完整,实际上也有负面作用:一方面很多程序集对你项目来说是用不上的,另一方面给跨平台的兼容性造成隐患,所以,要认真请理引用,让引用的程序集数量“最小化”。 556 | 557 | 具体的办法是在VS中打开“NuGet包管理器”,把用不上的引用卸载掉,比如“Microsoft.CodeDom.Providers.DotNetCompilerPlatform”这个引用就应该卸载掉。 558 | 559 | (4)清理网站bin文件夹: 560 | 561 | 你的ASP.NET程序是在Windows上开发的,用的dll是Windows的,所以,为了更好地保证跨平台的兼容性,你 **必须消除网站bin文件夹中多余程序集**,精简dll数量。原则上,Jexus已经自带了的dll(在Jexus的“runtime/lib/mono”文件夹下),网站bin文件夹中就不应该再出现同名同版本的。要尽可能减少从windows(VS开发环境)中带过来的dll,提高兼容性,减少跨平台风险。 562 | 563 | 清理网站bin文件夹的最简单办法是“排除法”:首先删除网站bin文件夹中的所有文件(项目自身的dll除外),然后通过浏览器访问该网站,查看错误页,错误页中提示差什么dll,你就上传什么dll,反复重复这个步骤,直到网站不再提示缺少某个dll为止。 564 | 565 | **6、AppHost与AppHost.Port有什么区别?** 566 | 567 | 相同之处是,AppHost与AppHost.Port都具有向本地指定端口进行数据转发的能力,不同之处在于,AppHost.Port只负责向指定的端口转发数据,而AppHost不仅具有向指定端口转发数据的功能,而且同时具备启动、管理、守护WEB应用程序的能力。AppHost.Port只是AppHost的一个子集,它更适合于向已经用其他方式启动并运行着的WEB应用程序提供全站式的反向代理服务。 568 | 569 | *\* 需要注意的是,AppHost和AppHost.Port不要同时启用。* 570 | 571 | **7、透明代理有什么好处,如何在反向代理中设置透明代理?** 572 | 573 | 透明代理最明显的好处是,应用程序可以直接获取访问者的IP地址。要在反向代理中启用“透明代理”,只需在反向代理配置项中添加字串“Transparent”属性标记就行。 574 | 575 | *\* 注:在Jexus中,设置透明代理是很容易的事,但是,透明代理能不能正常使用,还涉及到服务器物理网络配置方面的复杂内容,如果需要使用,请与Jexus作者联系。* 576 | 577 | **8、反向代理时,如何把域名传给后端目标服务器?** 578 | 579 | 在反向代理配置中,添加“HostFixed”或者“Host=Fixed”字串作为属性标记 580 | 581 | **9、由于IP端口数有限,如何让Jexus支持超大并发的反向代理?** 582 | 583 | 当反向代理服务器向后端目标服务器转发数据时,每一个连接都将占用一个本地端口,而一个IP地址上只能绑定65535个端口,因此,解决超过65535个并发连接的办法是添加更多的本地IP地址。 584 | 585 | 自V7.1版本开始,Jexus已经在反向代理服务中启用多地址绑定功能,具体的设置是:在网站配置文件中,将可以使用的面向后端的本地IP地址列表添加到 forward_bind 项。 586 | 587 | **10、为什么说不要在ASP.NET会话过程中开启多线程或使用async/await异步操作?** 588 | 589 | 简单地说,就是为了保证ASP.NET上下文的连贯性,让运行时正确把握会话的生命周期。如果我们在处理请求过程中使用了多线程等异步操作,ASP.NET引擎将失去对该会话的运行状态和生命周期的把握,其直接后果就是在会话处理并没有真正完成之前就提前结束了会话。 590 | 591 | **11、使用HttpClient对象与远程https服务器连接时出现验证失败的错误,怎么处理?** 592 | 593 | 一般来说,新安装的Linux系统的SSL根证书是不完整的也不是最新的,所以,当我们用HttpClient之类的组件访问某些https网站时就可能出现SSL握手失败的情况,这时,我们可以有两种处理方式: 594 | 595 | 处理办法一:更新根证书。Jexus文件夹中自带了根证书更新程序“cert-sync”,它的参数是操作系统根证书的路径和文件名,不同的系统有不同的位置。比如: 596 | 597 | ``` 598 | Ubuntu等Debian系操作系统: 599 | sudo /usr/jexus/cert-sync /etc/ssl/certs/ca-certificates.crt 600 | CentOS等RedHat系操作系统: 601 | sudo /usr/jexus/cert-sync /etc/pki/tls/certs/ca-bundle.crt 602 | ``` 603 | 604 | 处理办法二:对HttpClient设置参数,让其忽略服务器端证书验证。可参考下边的代码进行: 605 | ``` 606 | var handler = new System.Net.Http.HttpClientHandler(){ 607 | ServerCertificateCustomValidationCallback = (a, b, c, d) => true 608 | }; 609 | var client = new System.Net.Http.HttpClient(handler); 610 | ``` 611 | 612 | **12、Jexus在同一个端口号能支持配置多少个网站?** 613 | 614 | Jexus在同一个端口号可以支持配置无数个网站。为了区分不同的网站,这种同一端口多个网站的情况下,你必须对每个网站设置不同的域名或者不同的虚拟路径,这样,当用户访问时,Jexus才能知道将用户发过来的请求交给哪一个网站处理。 615 | 616 | **13、网页出现乱码是怎么回事,应怎么解决?** 617 | 618 | 网页出现乱码可能是由两方面的原因造成的。第一种可能性是网页声明的字符集与实际用的字符集不统一,比如\中指明正文的charset是utf8,但是网页实际用的却是GB2312;第二种可能性是数据库存储文本的格式用的字符集也网页所用的字符集不同。 619 | 620 | 解决的办法:网页字符集声明(如果有的话)、网页本身以及网页对应的cs文件、数据库文本,全部统一使用UTF8格式。 621 | 622 | 623 | ## 七、授权与交流 624 | 625 | (一)Jexus的使用授权与相互支持 626 | 627 | 1、Jexus 6.x及之前的版本属免费软件,任何人可以自由下载、传播和使用,但Jexus作者不对Jexus的用途、作用、效果、技术支持以及其它相关内容作任何明确或暗含的承诺,不承担任何直接或间接的责任。 628 | 629 | 2、Jexus v7.0及更高版本,允许个人或者20人以下的中小型团队免费使用,也允许WEB项目开发厂商作为测试环境免费使用。其它情况下使用,原则上需要获得Jexus作者刘冰先生(网名:宇内流云)的商业授权。 630 | 631 | 3、Jexus作者对所有Jexus版本提供技术支持或定制开发等有偿服务。 632 | 633 | 4、为了让Jexus可持续发展,欢迎广大用户及热心朋友对作者提供力所能及的支持,包括但不限于: 634 | 635 | (1)您可以利用各种机会宣传Jexus优良品质、实用价值及成功案例,分享使用心得和技术理论,为推广、普及Jexus付出努力; 636 | 637 | (2)您可以为初学者解难答疑; 638 | 639 | (3)你可以将Jexus的一些重要的技术文档翻译为英文或其它文字并公开、免费发布,为Jexus跨出国门走向国际添砖加瓦; 640 | 641 | (4)您可以利用开发新工程或升级旧工程的机会,利用Jexus把原来部署在windows上的web应用部署到Linux上,拓展Jexus的使用量; 642 | 643 | (5)您可以通过Jexus社区、QQ群、邮箱等各种渠道及时反馈Jexus的BUG,或者向Jexus的开发者、维护者提供富有建设性意义的各种建议; 644 | 645 | (6)您可以为Jexus提供广告支持,或者向Jexus作者提供力所能及的经费赞助,为Jexus的持续发展提供动力源泉。 646 | 647 | (二)Jexus官网、社区及联系方式 648 | 649 | 官方网站:www.jexus.org 650 | 651 | 项目仓库:[\[GitHub\]](https://github.com/yunekit/jexus)  [\[Gitee\]](https://gitee.com/yunekit/jexus) 652 | 653 | QQ群号码:700864320、102732979 654 | 655 | 邮箱地址:j66x@163.com 656 | -------------------------------------------------------------------------------- /docs/manual_v8.md: -------------------------------------------------------------------------------- 1 | # Jexus 用户手册 2 | 3 | *version: 8.0 for jexus 8.x* 4 | 5 | **作者:宇内流云(j66x@163.com)** 6 | 7 | ----------------------------------------------------------------------- 8 | 9 | 目 录 10 | 11 | 一、Jexus 概述 [......](#一jexus-概述) 12 | 13 | 二、Jexus 的安装与更新 [......](#二jexus-的安装与更新) 14 | 15 | 三、Jexus 的操作命令 [......](#三jexus-的操作命令) 16 | 17 | 四、Jexus 的全局配置 [......](#四jexus-的全局配置) 18 | 19 | 五、Jexus 的网站配置 [......](#五jexus-的网站配置) 20 | 21 | 六、问 答 [......](#六问-答) 22 | 23 | 七、授权与交流 [......](#七授权与交流) 24 | 25 | 26 | ## 一、Jexus 概述 27 | 28 | Jexus 是“Jexus Web Server”的简称,可缩写为“JWS”,中文读音近似于“杰克瑟斯”,也可称着“杰克斯”。 29 | 30 | Jexus是Linux平台上具有高安全性、高可靠性、高稳定性的高性能WEB服务器、反向代理网关服务器和自宿主WEB应用程序的托管(容器)服务器。 31 | 32 | Jexus除了具备通用WEB服务器所必备的静态文件处理和缓存功能外,还直接支持ASP.NET WEB应用程序,支持PHP,支持反向代理。自6.0版开始,Jexus开创性地设计了“AppHost”功能(HTTP应用程序容器),通过此功能,Jexus可以为Asp.Net Core、Tomcat、GoLang Web、Python Web等“自带HTTP功能的WEB应用程序”提供一体化集成管理。 33 | 34 | 就中国而言,Jexus是一款完全由中国人自主研发的具有原创性特质的国产服务器软件,支持国产操作系统和国产CPU芯片,真正做到了“安全、可靠、可信、可控”,具备我国党政机关和重要企事业单位信息化建设所需要的关键品质。 35 | 36 | **Jexus 有如下特点:** 37 | 38 | 1、性能强劲: 39 | 40 | 在操作系统支持方面,Jexus的设计目标非常明确:只考虑支持Linux和国产操作系统。正因为目标明确,定位清晰,针对性极强,所以在设计过程中完全不需要考虑兼容其它操作系统而不得不编写的大量冗余代码,也无需使用低性能的折衷方案,而是直接使用Linux的长项技术,比如epoll技术、sendfile零拷贝技术等等,核心技术直通操作系统。在编程技术方面,Jexus使用了c/c++与c#的混合编程技术,.Net Native技术,从语言层面为Jexus提供了坚实的保证。 41 | 42 | 2、安全可靠: 43 | 44 | 安全性是Jexus有别于其它Web服务器(比如Nginx、Apache、IIS等)的一个重要特色。Jexus初始开发于2006年,2008年在www.linuxdot.net上公开发布1.0版,最初开发的目的是为某政府网站提供Asp.Net跨平台支持,因此,Jexus从诞生开始,就已将其安全性作为开发的首要目标,不但在编码中努力避免漏洞和安全缺陷,而且还开创性地内置了"入侵检测和控制"技术,并在长期的运行实践中不断地完善其策略,优化其算法,为Jexus托管的网站提供了强有力的安全保护。 45 | 46 | 可靠性是Jexus另一个突出优点。可靠性主要表现在两个方面,一是稳定性,二是"可信可控"特性。在稳定性方面,由于Jexus采用了独特的混合编程技术,通过语言层面的扬长避短,在高性能与高可用性方面得到了平衡。另外,10多年来,Jexus在各种生产场景中摸爬滚打,在"实战"环境中不断地发现问题解决问题,不断地完善,保证了Jexus持续可靠的稳定性。在"可信可控"特质方面,Jexus是我国唯一一款由中国人(宇内流云/刘冰先生)从0开始原生开发的国产WEB服务器,整个Jexus程序系统没有使用任何特殊的第三方组件,甚至连开源代码也基本上没有"参考"使用,是纯粹的"中国血统",从程序开发者身份到程序源代码、程序运行环境等方面确保了Jexus"安全可靠""可控可信"的"信创"品质。 47 | 48 | 3、功能强大: 49 | 50 | 首先,Jexus是一款如同Nginx、Apache那样的"通用web服务器",具有通用web服务器必要的功能,除此之外,Jexus还具备如下功能特征: 51 | 52 | (1)支持ASP.NET。在Linux上支持Asp.Net运行,是Jexus的重要的特色功能之一(甚至一度被部分用户误认为Jexus就是一款单纯的Asp.Net服务器)。Jexus直接支持.NET Framework 3.5--4.8范围内的所有版本的Asp.Net Web程序。Jexus6.0以上版本已经集成.NET运行环境,服务器无需另行安装mono之类的.NET运行时。 53 | 54 | (2)提供Fast-CGI协议支持。通过Fast-CGI,Jexus能支持包括PHP在内的所有拥有Fast-CGI服务接口的WEB应用; 55 | 56 | (3)具有强劲的反向代理功能。Jexus支持全站反向代理、部分路径反向代理,支持多目标服务器反向代理(负载均衡),支持TCP“穿透”技术。Jexus的反向代理配置简洁灵活,性能好,功能强。 57 | 58 | (4)"AppHost"(自宿主WEB应用程序托管容器)是Jexus独创功能,它的作用是将ASP.NET Core或Node.js、Tomcat、Golang Web等“自宿主(自带HTTP服务)”型的以控制台方式运行的web应用程序以Jexus子进程的形式纳入Jexus统一管控与守护,而无需进行反向代理设置,也无需采用其它方式对这些目标程序进行运行和守护; 59 | 60 | (5)具备基于标准的正则表达式语法的强大的“URL重写”功能; 61 | 62 | (6)支持HTTPS,具有SSL(TLS v1.0-1.3)加密数据安全传输能力; 63 | 64 | (7)**支持“国密”**,这对于我国“信创”建设具有重要的意义; 65 | 66 | (8)直接内置“**入侵检测**”安全模块,具有入侵检测和防御功能,能自动识别和阻止非法请求。这是Jexus有别于其它Web服务器的重要的特色功能之一; 67 | 68 | (9)支持符合OWIN标准的WEB应用程序,如Nancy、SignalR等符合OWIN标准的应用框架,支持WebSocket; 69 | 70 | (10)支持包括龙芯在内的国产CPU和国产操作系统。 71 | 72 | 73 | ## 二、Jexus 的安装与更新 74 | 75 | (一)安装前的准备工作: 76 | 77 | 1、GLIBC版本:目前,Jexus v8 的编译环境是CentOS7.9,对GLIBC版本的最低要求是2.17,因此,Jexus应该在CentOS 7.9、Ubuntu 16.04以上版本或者满足GLIBC版本要求的国产操作系统上运行。 78 | 79 | 2、依赖库:Jexus使用了.NET NativeAOT技术,集成了Mono环境,因此,安装Jexus的Linux系统需要存在或安装libicu、libkrb5函数库。常用Linux一般默认已经安装了libkrb6或libicu,部分系统需要手工安装。 80 | 81 | 3、管理员身份:Jexus的安装、启动、停止等操作,操作者必须具备服务器管理员身份(即能使用su或sudo等命令提供root权限)。 82 | 83 | (二)在线安装: 84 | 85 | 指的是服务器能够连接互联网,服务器通过互联网从Jexus官方网站获取最新正式版安装包进行自动安装。安装命令是: 86 | 87 | - X86_64(amd64)系统: 88 | 89 | ``` 90 | curl https://jexus.org/release/x64/install.sh | sudo sh 91 | ``` 92 | 93 | - AARCH64(arm64)系统: 94 | 95 | ``` 96 | curl https://jexus.org/release/arm64/install.sh | sudo sh 97 | ``` 98 | 99 | - 龙芯(LoogArch64)系统: 100 | 101 | ``` 102 | curl https://jexus.org/release/la64/install.sh | sudo sh 103 | ``` 104 | 105 | *\* 强调:使用上述命令前应该确定自己当前身份是否是root身份。* 106 | 107 | (三)离线安装: 108 | 109 | 指的是从jexus官方网站下载Jexus安装包进行安装。这种安装方式常常用于服务器无法访问互联网的内网服务器。 110 | 111 | Jexus安装包是一个"tar.gz"压缩包,因此,"离线安装"过程,其实就是下载安装包,传输到目标服务器,解压到工作目录这样的操作过程。 112 | 113 | 1. 下载:到www.jexus.org下载你需要的Jexus安装包。 114 | 115 | 2. 将安装包上传到服务器的安装目录。Jexus习惯上使用"/usr/jexus"作为工作目录,因此,建议你把安装包上传到"/usr/"文件夹中。 116 | 117 | 3. 解压:在jexus安装包所在文件夹中,以root身份解压Jexus压缩包(安装包)。如:sudo tar -zxvf jexus-8.0.x-x64.tar.gz 118 | 119 | 4. 删除安装包。 120 | 121 | 5. 从备份文件夹中恢复之前Jexus的配置文件(如果有)。 122 | 123 | (四)关于Jexus的更新或重装: 124 | 125 | 由于Jexus是绿色软件,所谓的"安装"其实就是下载和解压,因此,Jexus的更新、重装,其实与上述安装过程是相似的,不同点就是在安装之前多两个步骤: 126 | 127 | (1)停止Jexus运行; 128 | 129 | (2)删除或更名(备份)之前的Jexus的工作文件夹。 130 | 131 | (五)检查版本和初始化: 132 | 133 | 1、检查Jexus版本号的命令是“/usr/jexus/jws -V”,如果Jexus安装正常,此命令将打印出Jexus的版本号以及集成的 mono.net 运行时的版本号。 134 | 135 | 2、初始化命令是“sudo /usr/jexus/jws init”,这是安装Jexus后的非必要操作,但是建议执行一次。 136 | 137 | 138 | ## 三、Jexus 的操作命令 139 | 140 | (一)基本操作命令: 141 | 142 | Jexus核心命令是“jws”,如果你把jexus安装在“习惯位置”,即“/usr/jexus/”文件夹中,那么,含绝对路径的完整命令将是“/usr/jexus/jws”。 143 | 144 | “jws”其实是一个shell脚本文件,具体内容可以自行查看。 145 | 146 | Jexus的操作是通过为“jws”提供不同的操作参数实现的,包括“启动(start)”“停止(stop)”“重启(restart)”“运行状态(status)”“初始化(init)”“显示版本(-V)”。初始化和显示版本号已经在前文进行了说明,不再此处复述。 147 | 148 | 1、启动命令 149 | 150 | (1)启动Jexus程序并同时启动所有的网站: 151 | 152 | sudo /usr/jexus/jws start 153 | 154 | (2)在Jexus运行期间单独启动一个网站: 155 | 156 | sudo /usr/jexus/jws start 网站名 157 | 158 | *\* 注:"网站名"在Jexus中,指的是网站配置文件名,jexus的网站配置文件即是"网站名"。* 159 | 160 | 2、停止命令 161 | 162 | (1)停止Jexus程序同时停止所有的网站: 163 | 164 | sudo /usr/jexus/stop 165 | 166 | (2)停止一个指定的网站(web应用程序) 167 | 168 | sudo /usr/jexus/stop 网站名 169 | 170 | 3、重启命令 171 | 172 | (1)重启jexus程序和所有的网站程序 173 | 174 | sudo /usr/jexus/jws restart 175 | 176 | (2)重启指定的网站程序 177 | 178 | sudo /usr/jexus/jws restart 网站名 179 | 180 | 4、显示Jexus是否处理于运行状态 181 | 182 | sudo /usr/jexus/jws status 183 | 184 | 注意:以上命令都加有sudo命令,指的是以管理员身份发起操作,如果你本来就是管理员身份,就不必使用sudo。 185 | 186 | (二)关于开机启动: 187 | 188 | 让Jexus实现开机启动,有两个办法(强调:只能选其中之一): 189 | 190 | **方法一**:通过“/etc/rc.local”进行。 191 | 192 | 具体方法是:在“/etc/rc.local”文件中加入“/usr/jexus/jws start”一行。 193 | 194 | 此方法使用简便,但要注意:一是要把jws的路径和文件名写正确,要使用“jws”命令文件的绝对路径;二是rc.local文件要有"可执行"属性,建议运行一下这个命令:“sudo chmod +x /etc/rc.local”。 195 | 196 | **方法二**:将jexus注册到systemd中成为系统服务。 197 | 198 | 具体方法如下: 199 | 200 | 1、注册服务:sudo systemctl enable /usr/jexus/jws.service 201 | 202 | 2、启动服务:sudo systemctl start jws 203 | 204 | 3、停止服务:sudo systemctl stop jws 205 | 206 | 4、重启服务:sudo systemctl restart jws 207 | 208 | 5、注销服务:sudo systemctl disable jws 209 | 210 | *\*\*\* 特别强调:一旦jexus被注册为系统服务之后,jexus的启动、停止、重启等操作只能使用systemctl命令进行操作,不能再使用不带网站参数的“jws start”“jws stop”“jws restart”等命令进行操作。* 211 | 212 | 213 | ## 四、Jexus 的全局配置 214 | 215 | **1、jws.conf 配置** 216 | 217 | 在jexus的工作文件夹中(一般是"/usr/jexus")有一个基本的配置文件,文件名是"jws.conf",这是Jexus的最重要的全局配置文件。 218 | 219 | 主要配置项如下: 220 | 221 | SiteConfigDir:指定网站配置文件夹的路径,默认配置是jexus程序文件夹下的"siteconf"子目录。该项配置不建议修改。 222 | 223 | SiteLogDir:指定jexus日志和网站访问日志的存放路径。默认配置是jexus程序文件夹下的"log"子目录。该项配置不建议修改。 224 | 225 | Httpd.Processes:指的是jexus的httpd工作进程的数量(不是ASP.NET工作进程数量),默认为1,设为0表示自动,即由jexus根据服务器cpu数量自行确定。 226 | 227 | *该项配置能影响Jexus处理html等静态文件以及数据转发等方面的性能,其具体数量应该结合服务器cpu数量、内存富余度和并发请求数量综合考虑,一般来情况下配置2个进程就行,如果服务器负载重、并发量大、cpu核心数量多,可以按cpu核心数的四分之一(最多二分之一)设置。* 228 | 229 | Httpd.MaxTotalMemory:指的是允许Jexus的httpd工作进程最多使用多少内存,单为是"兆字节",设为0表示"自动"。 230 | 231 | Httpd.MaxCpuTime:表示Jexus的httpd工作进程最多能使用多少cpu时间,单位为秒,达到这个时间值后,jexus的httpd工作进程就会自动重启。设为0表示禁用这个选项(不建议启用这个先选项)。 232 | 233 | Httpd.MaxConnPerIp:表示jexus httpd工作进程允许每个IP地址能同时发出多少个TCP连接,当某IP地址超过这个数量的连接时,Jexus将拒绝接受。设为0表示禁用这个选项。 234 | 235 | *Jexus统计每IP发出的连接数是由与它连接的socket所反映的IP地址决定的,如果你在Jexus服务器前边没配备了其它反向代理服务器,Jexus只能得到该反向代理服务器的地址,这次架构下,请不要在Jexus上设置IP数限制。* 236 | 237 | Php-fcgi.Set:Jexus的PHP FastCGI管理器配置。本设置的值分两部分,两个部分之间用英文逗号分开。第一部分是指定php-cgi命令文件的完整路径(一般是"/usr/bin/php-cgi"),第二部分是设定php-cgi的最大工作进程数量。本项设置不是必须的,如果你服务器没有PHP网站或者有其它的方式提供PHP环境(比如使用了php-fpm),就不必启用该选项。 238 | 239 | **2、mime.conf 配置** 240 | 241 | Jexus 中,描述数据类型的配置文件是 jexus 根文件夹中的 mime.conf 文件,它是一个文本文件,可以使用 vim 等任何文本工具进行编辑修改。 242 | 243 | mime.conf 文件中,每一行代表一个类型的数据,由“文件扩展名”和“数据类型描述”两部分组成,两部分由英文冒号分开,如: 244 | 245 | ``` 246 | wasm: application/wasm 247 | ``` 248 | 249 | 250 | ## 五、Jexus 的网站配置 251 | 252 | Jexus Web Server 可以同时运行多个站点,没有数量限制。 253 | 254 | 一个具体的站点,由"端口""域名""虚拟根路径"三个基本要素确定,三个要素中任何一项不同,即表示不同的站点。 255 | 256 | 必须把所有网站配置文件放到全局配置文件"jws.conf"所指定的网站配置文件夹内(默认是jexus工作目录内的"siteconf"子文件夹)。这个文件夹除了网站配置文件,不能有其它任何文件,因为jexus会认为该文件夹中的任何一个文件都代表着一个具体的网站配置(子文件夹除外,不在jexus搜索之列)。 257 | 258 | 每个网站有且只有一个配置文件,配置文件的文件名代表这个网站的名称,因此配置文件的文件名应该尽量具有网站个性特点,以便管理员容易记忆和识别。要特别注意:文件名不能有空格!支持中文,但不建议使用。 259 | 260 | 一个网站可以拥有任意多的域名或"泛域名",不同网站不能有相同的域名,每个端口最多只能有一个没有域名的网站(域名栏填"\*"号)。在80端口,这个没有域名的网站叫做"默认网站"。 261 | 262 | (一)网站配置文件的基本配置项: 263 | 264 | port:站点服务端口,这是必填项。http的标准端口是80,https的标准端口是443,最大值是65535。 265 | 266 | root:根路径。指定网站虚拟根路径(URL根路径)和存放网站文件的物理文件夹路径(物理根),两种路径用一个以上英文空格符分开。URL根路径常常是“/”。如果你的网站文件存放在“/var/www/default”这个文件夹中,那么,root项就应该是“root=/ /var/www/default”。 267 | 268 | **root 项是必填项**。如果遇上在当前服务器并没有网站程序的"全站反向代理"这种情况,你可以在服务器单独创建一个空白文件夹并设定它,也可以设为"/tmp"。 269 | 270 | hosts:配置网站的域名。一个站可以指定多个域名,各域名间用英文逗号分隔。也可以配置“泛域名”代指某个域名下的所有子域名,比如“\*.mysite.com”。如果填“\*”号,就表示访问这个站可以是任意的域名或是服务器IP地址,即,区分这个网站不是用域名区分,而是用端口或虚拟根路径进行区分。 271 | 272 | (二)网站配置文件的拓展(高级)配置 273 | 274 | 一个网站配置了前述的三项基本选项一般就能正常运行了。本部分指的是基本配置之外的拓展(高级)配置,包括"反向代理""URL重写""端口转发""AppHost""IP地址黑(白)名单"等等。 275 | 276 | 具体说明如下: 277 | 278 | **1、默认首页(Indexes):** 279 | 280 | Jexus已经内置默认首页的定义,包括index.html、index.htm、default.html、default.htm、index.aspx、default.aspx。如果你网站的首页没有被包括在这些默认首页中,可以通过启用该选项指定你的默认首页。 281 | 282 | **2、URL重写(rewrite):** 283 | 284 | "URL重写"是指WEB服务器将访问者向服务器发起的URL请求按指定的匹配规则解释和匹配到另外的一个真实RUL路径资源上。比如,当访问者访问".php"、".asp"等类型的文件时,服务器以 "/404.html" 这个URL应答,你可以设为: 285 | ``` 286 | rewrite=^/.+?\.(asp|php|cgi)$ /404.html 287 | ``` 288 | rewrite格式: 289 | 290 | "rewrite="的后面是两部分阻成,两部分之间由一个空格分开。 291 | 292 | 空格前是匹配的条件:用正则表达式描述URL的匹配条件。 293 | 294 | 空格后是匹配的目标:指的是如果用户访问的路径合乎前面的匹配条件时,服务器将以哪个规则回应。 295 | 296 | 如: 297 | 298 | 把"/bbs"匹配到"/bbs/index.aspx",把"/bbs/file-编号" 匹配到"/bbs/show.aspx?id=编号": 299 | ``` 300 | rewrite=^/bbs$ /bbs/index.aspx 301 | rewrite=^/bbs/file-([0-9]{1,6})$ /bbs/show.aspx?id=$1 302 | ``` 303 | 格式解释:rewrite的等号后含有两部分内容,用空隔分开。前半部分是一个正则表达式,用于描述需要URL重写的(用户浏览器中的)url路径样式,后半部分是当用户的URL合乎前面的正则表达式时,JWS应该重写和访问的真实URL路径。 304 | 305 | **3、禁止或允许某IP或IP段访问网站(denyfrom、allowfrom)**: 306 | 307 | A、只允许某些IP地址访问网站(白名单功能) 308 | 309 | 默认情况下,允许所有IP地址访问。如果手工设置IP地址白名单,那么,白名单之外的IP地址会自动划入黑名单而禁止访问服务器。 310 | 311 | 配置格式,形如: 312 | 313 | allowfrom=1.2.3.\*   \# 一个IP地址段 314 |
315 | AllowFrom=2.2.3.3   \# 一个具体的IPv4地址 316 | 317 | B、禁止某IP或某IP段访问网站(黑名单功能) 318 | 319 | 默认情况下,本配置为空。如果手工添加需要禁止访问的IP地址(段),必须合乎一个规则:黑名单必须是白名单的真子集。 320 | 321 | 配置格式,形如: 322 | 323 | denyfrom=111.222.111.\*   \# 一个IP地址段 324 |
325 | denyfrom=101.201.1.132   \# 一个IP地址 326 | 327 | *注:为了不使配置复杂化,建议只使用“黑名单”或只使用“白名单”。* 328 | 329 | **4、禁止访问某文件夹及其子文件夹中的内容(DenyDirs):** 330 | 331 | 格式是:DenyDirs=PATH 332 | 333 | PATH指的是相对于网站根文件夹的URL路径,如 "/abcfiles"或"~/abcfiles"。可以同时指定多个路径,用英文逗号分开。 334 | 335 | **5、是否对请求的URL参数等进行安全检测(CheckQuery)**: 336 | 337 | 本选项的值包括true和false之一,默认是true,即需要进行安全检查,除非你的确需要关掉这个选项,否则可以不填。格式如下: 338 | 339 | CheckQuery=false 340 | 341 | *注:关掉本项可以提高服务器速度,但就安全而言,不建议关掉它。* 342 | 343 | **6、NOFILE(无文件替代)功能:** 344 | 345 | 本功能是Jexus的特有功能,指的是如果服务器找不到真实文件时,服务器将用使用一个什么文件去替代。格式是: 346 | 347 | NoFile=PATH 348 | 349 | PATH:指定的替代文件和相当于网站根文件夹的完整路径,如: 350 | 351 | Nofile=/mvc/controller.aspx或者Nofile=/index.php 352 | 353 | (提示:替换后,Jexus会把原始请求的路径资源保存到一个名为"X-Real-Uri"的HTTP请求头中,以便目标文件作进一步处理) 354 | 355 | **7、关闭网站访问日志(NoLog)**: 356 | 357 | nolog=yes 358 | 359 | (注:禁用网站日志功能会轻微提高WEB服务器系统的处理速度,但不足也是明显的,就是你无法详细了解网站的访问情况了) 360 | 361 | **8、长连接开关(Keep_Alive)**: 362 | 363 | Keep_Alive=true或false 364 | 365 | 注:其值为true、false或yes、no,默认使用长连接(true),大多数情况可以不使用该选项,除非你认为真有必要禁止长连接。 366 | 367 | **9、反向代理功能(Reproxy):** 368 | 369 | 这是Jexus的一个重要功能。指的是当用户访问某个路径或任何路径时,Jexus将这个请求转发给其它的服务器。 370 | 371 | 通过Reproxy功能,我们可以实现负载均衡或"高可用性"。 372 | 373 | 格式是: 374 | 375 | Reproxy=请求路径 目标服务器IP:端口 | 附加参数,如: 376 | ``` 377 | Reproxy=/ http://1.1.1.1:800 #全站反代 378 | Reproxy=/ http://1.1.1.1:800 | HostFixed #带附加参数的反代 379 | Reproxy=/abc http://1.1.1.1:900/abc #部分路径反代 380 | Reproxy=/ http://1.1.1.1:5000, https://2.2.2.2:5000 #多目标反代 381 | ``` 382 | 383 | 值得强调的是,“多目标服务器的反向代理”将产生负载均衡(WEB集群)和高可用性效果,即,当用户访问该站时,Jexus就会随机选择一台后端服务器进行处理,如果Jexus发现某台服务器已经宕机而没有应答,Jexus就会把请求转发给其它剩余的服务器。 384 | 385 | *\* 需要注意:* 386 | 387 | 1. 当“部分反代”和“全站反代”多条规则同时出现时,一定要把“全站反代”那一条规则放到最后。 388 | 2. Jexus v8.0 开始支持对后端(上游)工作服务器为 HTTPS 协议的反向代理,在Reproxy配置中,如果目标路径不写“https://”,则默认为 HTTP 协议。 389 | 3. Jexus Reproxy配置行中支持“参数”,参数应放在最后,与主配置之间用英文“|”字符分隔。 390 | 4. Jexus 8.0的Reproxy配置还在进化中,目前的配置只对应当前的发行版。 391 | 392 | **10、透明反代(反代中的 transparent 参数):** 393 | 394 | 在反向代理设置项中,加上“Transparent”字串参数后,反向代理即成为“透明的”反向代理,即,后端服务器可以直接获得前端访问者的IP地址。 395 | 396 | *但透明代理是否有效,还将涉及到网络配置,更多相关表述请参数本手册“问答”部分。* 397 | 398 | **11、使用FAST-CGI提供的服务(FastCGI.Add):** 399 | 400 | 对于TCP连接的fast-cgi: 401 | 402 | fastcgi.add=php | tcp:127.0.0.1:9000 403 | 404 | 对于unix sockets连接的FastCGI: 405 | 406 | fastcgi.add=php,php3 | socket:/tmp/phpsvr 407 | 408 | **12、启用或关闭gzip压缩功能(UseGZIP):** 409 | 410 | UseGzip=true或false 411 | 412 | *\* 注:Jexus默认已经启用gzip压缩功能,所以,除非你希望禁止该功能,才需要显式地设置此选项。* 413 | 414 | **13、指定网站的用户身份(User):** 415 | 416 | 格式是:User=用户名 417 | 418 | "用户名"指的是Linux系统中已经存在的一个用户名称,比如 www-data。 419 | 420 | 默认情况下,jexus将使用"root"作为网站工作进程的用户身份,这种身份的权限很高,为了网站更加安全,建议指定一个权限低的用户身份,而"www-data"是大多数Linux系统的内置的专为网站准备的一个用户名,其权限较低,适合指定给网站使用。 421 | 422 | **14、启用HTTPS进行SSL安全传输(UseHttps):** 423 | 424 | 要为网站启用https,首先需设置UseHttps=ture选项,表示该站启用Https安全连接。另外,对于https,网站端口应该设为443(https默认端口)。 425 | 426 | 然后是设置SSL证书、私钥、协议版本、加密套件。 427 | 428 | (1)设置证书文件: 429 | 430 | ssl.certificate=完整的证书路径和文件名 431 | 432 | (2)设置密钥文件: 433 | 434 | ssl.certificatekey=密钥文件的完整路径和文件名 435 | 436 | (3)协议版本: 437 | 438 | ssl.protocol=版本列表 439 | 440 | 指的是ssl及tls协议及版本号,包括:TLSv1.0、TLSv1.1、TLSv1.2和TLSv1.3。可以同时多个版本号,各版本号之间用空格分隔。 441 | 442 | *\* 该选项不是必填项,但为了安全起见,建议启用并只选择TLSv1.1、TLSv1.2、1.3三个版本号,甚至只使用TLSv1.2、TLSv1.3两个版本号。* 443 | 444 | (4)加密套件: 445 | 446 | 指定SSL/TLS加解密过程中使用和不使用的算法套件的列表,格式是“ssl.ciphers=套件列表”,如: 447 | ``` 448 | ssl.ciphers=ECDHE-RSA-AES256-GCM-SHA384:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE 449 | ``` 450 | 451 | *本选项不是必填项。* 452 | 453 | **15、ASP.NET网站专用配置:** 454 | 455 | (1)配置Asp.Net工作进程数量(默认为1): 456 | 457 | 格式为:AspNet.Workers=工作进程数 458 | 459 | 默认情况下,Jexus为Asp.Net提供1个工作进程,单工作进程的好处是节约内存,但也有弱点,比如,难以充分发挥多CPU(多核)的性能优势,大并发承受力、容灾能力等方面比多工作进程弱等等。 460 | 461 | 为Asp.Net开启"多进程"工作模式后,ASP.NET网站的web.config中的Session状态服务不能再使用"InProc"模式,而应该使用"StateServer"等其它非程序内存贮模式,否则极易出现Session数据丢失问题。 462 | 463 | 另外,当Asp.Net使用多进程并行处理时,为了在验证数据的加密、解密方面保证一致性,还应该在web.config文件夹中的"system.web"节中配置"machineKey"项。例如作如下配置: 464 | 465 | ``` 466 | 470 | ``` 471 | 472 | 设置ASP.NET多进程数量并非越多越好,要综合考虑服务器CPU核数、可用内存、并发压力等多方面因素。一般来说,进程数量配置为CPU核数的一半就行了,最多不要超过CPU的核心数。 473 | 474 | (2)限制ASP.NET消耗CPU的时间数(默认为0,无限制): 475 | 476 | 格式是:AspNet.MaxCpuTime=单位为秒的时间值 477 | 478 | 配置该选项后,当该网站使用cpu的时间累积到你指定的最大值之后,Jexus将重启这个网站。 479 | 480 | (3)限制Asp.Net使用内存的最大值(默认为0,无限制): 481 | 482 | 格式是:AspNet.MaxMemory=以兆为单位的内存数 483 | 484 | 设置该选项后,当你的网站程序使用的内存超过指定的最大值时,Jexus将重启该站,释放之前占用的内存空间。 485 | 486 | **16、WEB应用程序端口转发(AppHost.Port):** 487 | 488 | 这是Jexus特有功能。 489 | 490 | 格式是:AppHost.Port=端口号 491 | 492 | 本项配置指的是将当前网站的请求数据全部转发到本机某WEB应用程序侦听端口,比如把请求转发到Asp.net Core应用程序的5000端口上。 493 | 494 | 可以使用多个端口,各端口号之间用英文逗号分开。 495 | 496 | 端口转发与反向代理功能相近,但端口转发的性能更高。 497 | 498 | 注意:一是端口转发不能用于虚拟根路径不是"/"的网站;二是端口转发只能在同一服务器上进行。 499 | 500 | **17、自宿主WEB应用程序托管服务(AppHost):** 501 | 502 | “自宿主web应用程序托管”是Jexus的一项独有的富有创造性的重要功能,它为Asp.Net Core的Kestrel、Java的Tomcat甚至Node.js、golang web等自带Http服务的“自宿主Web应用程序”提供了简单可靠、可控的一体化集成管理。 503 | 504 | 格式:AppHost=\{Cmd=命令及参数; AppRoot=程序文件夹路径; AppPort=应用程序端口号\} 505 | 506 | 也可以分行书写(分行书写时,“AppHost={”必须在同一行),例如: 507 | 508 | ``` 509 | AppHost={ 510 | Cmd=命令及参数; 511 | AppRoot=应用程序文件夹路径; 512 | AppPort=应用程序端口号; 513 | } 514 | ``` 515 | 子配置项说明: 516 | 517 | - Cmd:必选项。表示启动这个web应用程序的命令和命令参数,如 Cmd=dotnet /var/www/mysite/webapp.dll。 518 | 519 | - AppRoot:非必选项。表示这个应用程序所在的文件夹路径,如:AppRoot=/var/www/mysite。如果本项没有指定,Jexus将使用网站root项中的根文件夹路径作为应用程序路径。 520 | 521 | - AppPort:必填项。表示这个应用程序使用的侦听端口,多个端口用英文逗号分隔。特别强调,AppHost中的port不能与网站的port相同。 522 | 523 | - Env:可选参数。表示这个应用程序工作时需要的环境变量,如Env=(PATH=/myhost/bin:$PATH),多个设置用英文逗号分开。 524 | 525 | - ErrLog:可选项。表示将这个应用程序的异常输出重定向到指定的文件(需填写完整路径); 526 | 527 | - OutLog:可选项。表示将这个应用程序的控制台输出重定向到指定的文件(需填写完整路径); 528 | 529 | - User:可选项。以指定的用户身份运行该应用程序,默认为root身份。 530 | 531 | 注意: 532 | 533 | 1、AppHost像AppHost.Port一样,不支持虚拟路径; 534 | 535 | 2、AppHost功能是将指定的具有http服务能力的web应用程序纳入jexus工作进程进行管理,对Asp.Net Core或tomcat、spring、node.js等自宿主web应用程序用于生产环境时具有重要的意义。 536 | 537 | 3、不要同时启用AppHost和AppHost.Port。 538 | 539 | 540 | **18、OWIN 配置** 541 | 542 | Jexus支持各种符合OWIN协议的 .NET WEB 应用。OWIN应用应该添加一个“适配器”(一个.NET class)提供给Jexus调用,该适配器中必须包括一个名叫 OwinMain 的公共方法,该方法是Jexus与OWIN WEB应用进行数据交换的核心通道。 543 | 544 | *有关OWIN应用及适配器代码编写方面的问题和技术,由于专业性较强,需要进一步了解的朋友可以与Jexus作者联系。或参考github上的Jexus开源项目中的 [OwinTest](https://github.com/yunekit/jexus/tree/main/Demos.src/OwinTest) 的源代码* 545 | 546 | 具备“适配器”这个前提后,网站开启OWIN应用的办法是在网站配置文件中,添加 OwinMain 项,这个项的值就是具有OwinMain方法的类库(程序集)的名称,如: 547 | 548 | ``` 549 | OwinMain = MyOwinApp.dll 550 | ``` 551 | 552 | ## 六、问 答 553 | 554 | Jexus从2008年正式公开发布以来,一直受到了国内外Web建设者们的关注,涉及到了各种规模和不同类型的项目,通过与开发者们的互动,我们得到比较丰富的使用经验和问题处理经验。本章节我们以问答的形式向大家共享一些具有普遍性的经验或常识。 555 | 556 | **1、如何评估传统的Asp.Net是否能迁移到Linux平台?** 557 | 558 | 一个重要的观察点就是你的Asp.Net应用程序以及所引用的第三方库是否是“标准的”、“通用的”,即是否不依赖Windows系统所特有的功能、组件和系统函数。比如,不能使用Windwos特有的ActivX/COM技术,不能通过Dllmport方法调用windows特有的kernel.dll、user32.dll等函数库的函数。另外,要符合linux的文件与路径命名规则,比如文件名大小写敏感等。 559 | 560 | 561 | **2、传统Asp.Net在Linux平台上性能如何?** 562 | 563 | “传统Asp.Net”指的是有别于“Asp.Net Core”的基于传统的“.NET Framework(.NET 1.0-4.8.x)”运行环境的 .NET WEB应用程序框架。 564 | 传统的ASP.NET程序在Linux上运行是依赖“mono”这个开源的跨平台运行时及其相应的跨平台通用类库(Jexus已集成mono环境,不需要单独安装)。现在,mono在技术上已经成熟,性能也很优秀,但由于它需要照顾“跨平台”这个特殊需求,部分特性必须作出一些折中处理,因此部分性能可能略弱于只能在Windows系统上运行的.NET Framework。 565 | 566 | **3、为什么说Jexus是Asp.Net Core的最理想的宿主服务器?** 567 | 568 | Jexus通过“AppHost”配置支持ASP.NET Core应用程序。“AppHost”是Jexus专门针对那些自身具有http通讯功能的WEB应用程序设计的一项服务功能,该功能相当于一个容器,把HTTP自宿主应用程序作为Jexus的一个子进程纳入Jexus的统一管理、守护和进程间数据转发,ASP.NET Core等HTTP自宿主程序不再需要寻求其它方式运行和守护,具有维护简单,管理方便和数据转发高效稳定的优势。 569 | 570 | *\* 值得一提的是,Jexus不但是ASP.NET Core的理想宿主,也可以是tomcat、node.js等自带HTTP功能的WEB应用程序的理想容器,它们都可以纳入Jexus的AppHost容器进行管控和运行。* 571 | 572 | **4、作反向代理服务器,用Jexus还是用Nginx?** 573 | 574 | Nginx是一款在中国国内使用得比较广泛的一款优秀的反向代理服务器,与Nginx相同的是,Jexus同样也具有灵活高效的反向代理能力,与nginx不同的是,Jexus的反向代理具有安全检测功能,较nginx那种单纯的数据转发,Jexus的反向代理更加安全可靠。 575 | 576 | *\* 因此,从安全角度看,Jexus更适合做反向代理服务器、负载均衡网关服务器,更适合成为大型WEB项目面向公众一侧的“入口服务器”;从国内信息化建设角度看,Jexus作为一款性价比极高、安全性极强的具有完全自主知识产权的纯国产Web服务器中间件,更是“信创”建设中Web服务器、反向代理服务器最明智的首选项。* 577 | 578 | **5、迁移Asp.Net应用程序到Linux平台时要注意些什么?** 579 | 580 | (1)编译发布:建议使用预编译发布而不是直接将源码上传到服务器。 581 | 582 | (2)清理web.config: 583 | 584 | 确保你的web.config是使用的UTF-8字符集编码,如果不是UTF-8编码,必须去掉所有的中、日、韩等各种非英文文字。字符集方面的问题,是造成ASP.NET程序无法启动或web.config配置无效的主要原因。 585 | 586 | 将web.config中的所有“\\”号改为“/”号;将所有的windows路径字串改为符合linux规范的linux路径。 587 | 588 | (3)最小化引用: 589 | 590 | 用Visual Studio的ASP.NET模板创建的默认网站,VS会自动给你添加大量的程序集引用,大量的引用,看似更加完整,实际上负面作用也不小:一方面很多程序集对你项目来说是用不上的,浪费空间和内存,另一方面是会给跨平台的兼容性造成隐患,所以,要认真请理引用,让引用的程序集数量“最小化”。 591 | 592 | 具体的办法是在VS中打开“NuGet包管理器”,把用不上的引用卸载掉,比如“Microsoft.CodeDom.Providers.DotNetCompilerPlatform”这个引用就应该卸载掉。 593 | 594 | (4)清理网站bin文件夹: 595 | 596 | 你的ASP.NET程序是在Windows上开发的,用的dll是Windows的,所以,为了更好地保证跨平台的兼容性,你 **必须消除网站bin文件夹中多余程序集**,精简dll数量。原则上,Jexus已经自带了的dll(在Jexus的“runtime/lib/mono”文件夹下),网站bin文件夹中就不应该再出现同名同版本的。要尽可能减少从windows(VS开发环境)中带过来的dll,提高兼容性,减少跨平台风险。 597 | 598 | 清理网站bin文件夹的最简单办法是“排除法”:首先删除网站bin文件夹中的所有文件(项目自身的dll除外),然后通过浏览器访问该网站,查看错误页,错误页中提示差什么dll,你就上传什么dll,反复重复这个步骤,直到网站不再提示缺少某个dll为止。 599 | 600 | **6、AppHost与AppHost.Port有什么区别?** 601 | 602 | 相同之处是,AppHost与AppHost.Port都具有向本地指定端口进行数据转发的能力,不同之处在于,AppHost.Port只负责向指定的端口转发数据,而AppHost不仅具有向指定端口转发数据的功能,而且同时具备启动、管理、守护WEB应用程序的能力。AppHost.Port只是AppHost的一个子集,它更适合于向已经用其他方式启动并运行着的WEB应用程序提供全站式的反向代理服务。 603 | 604 | *\* 需要注意的是,AppHost和AppHost.Port不要同时启用。* 605 | 606 | **7、透明代理有什么好处,如何在反向代理中设置透明代理?** 607 | 608 | 透明代理最明显的好处是,应用程序可以直接获取访问者的IP地址。要在反向代理中启用“透明代理”,只需在反向代理配置项中添加字串“Transparent”属性标记就行。 609 | 610 | *\* 注:在Jexus中,设置透明代理是很容易的事,但是,透明代理能不能正常使用,还涉及到服务器物理网络配置方面的复杂内容,如果需要使用,请与Jexus作者联系。* 611 | 612 | **8、反向代理时,如何把域名传给后端目标服务器?** 613 | 614 | 在反向代理配置中,添加“HostFixed”或者“Host=Fixed”字串作为属性标记 615 | 616 | **9、由于IP端口数有限,如何让Jexus支持超大并发的反向代理?** 617 | 618 | 当反向代理服务器向后端目标服务器转发数据时,每一个连接都将占用一个本地端口,而一个IP地址上只能绑定65535个端口,因此,解决超过65535个并发连接的办法是添加更多的本地IP地址。 619 | 620 | 自V7.1版本开始,Jexus已经在反向代理服务中启用多地址绑定功能,具体的设置是:在网站配置文件中,将可以使用的面向后端的本地IP地址列表添加到 forward_bind 项。 621 | 622 | **10、为什么说不要在ASP.NET会话过程中开启多线程或使用async、await异步操作?** 623 | 624 | 简单地说,就是为了保证ASP.NET上下文的连贯性,让运行时正确把握会话的生命周期。如果我们在处理请求过程中使用了多线程等异步操作,ASP.NET引擎将失去对该会话的运行状态和生命周期的把握,其直接后果就是在会话处理并没有真正完成之前就提前结束了会话。 625 | 626 | **11、使用HttpClient对象与远程https服务器连接时出现验证失败的错误,怎么处理?** 627 | 628 | 一般来说,新安装的Linux系统的SSL根证书是不完整的也不是最新的,所以,当我们用HttpClient之类的组件访问某些https网站时就可能出现SSL握手失败的情况,这时,我们可以有两种处理方式: 629 | 630 | 处理办法一:更新根证书。Jexus文件夹中自带了根证书更新程序“cert-sync”,它的参数是操作系统根证书的路径和文件名,不同的系统有不同的位置。比如: 631 | ``` 632 | Ubuntu等Debian系操作系统: 633 | sudo /usr/jexus/cert-sync /etc/ssl/certs/ca-certificates.crt 634 | CentOS等RedHat系操作系统: 635 | sudo /usr/jexus/cert-sync /etc/pki/tls/certs/ca-bundle.crt 636 | ``` 637 | 638 | 处理办法二:对HttpClient设置参数,让其忽略服务器端证书验证。可参考下边的代码进行: 639 | ``` 640 | var handler = new System.Net.Http.HttpClientHandler(){ 641 | ServerCertificateCustomValidationCallback = (a, b, c, d) => true 642 | }; 643 | var client = new System.Net.Http.HttpClient(handler); 644 | ``` 645 | 646 | **12、Jexus在同一个端口号能支持配置多少个网站?** 647 | 648 | Jexus在同一个端口号可以支持配置无数个网站。 649 | 650 | **但要强调的是**: 651 | 652 | - 在“一个端口多个网站”的情况下,为了区分不同的网站,你必须对每个网站设置不同的域名或者使用不同的虚拟路径,这样,当用户访问时,Jexus才会知道将用户发过来的请求交给哪一个网站处理。 653 | - 同一个端口,不能同时支持Http和Https两种不同的传输协议。 654 | 655 | **13、网页出现乱码是怎么回事,应怎么解决?** 656 | 657 | 网页出现乱码可能是由两方面的原因造成的。第一种可能性是网页声明的字符集与实际用的字符集不统一,比如\中指明正文的charset是utf8,但是网页实际用的却是GB2312;第二种可能性是数据库存储文本的格式用的字符集也网页所用的字符集不同。 658 | 659 | 解决的办法:网页字符集声明(如果有的话)、网页本身以及网页对应的cs文件、数据库文本,全部统一使用UTF8格式。 660 | 661 | 662 | ## 七、授权与交流 663 | 664 | (一)Jexus的使用授权与相互支持 665 | 666 | 1、Jexus 6.x及之前的版本属免费软件,任何人可以自由下载、传播和使用,但Jexus作者不对Jexus的用途、作用、效果、技术支持以及其它相关内容作任何明确或暗含的承诺,不承担任何直接或间接的责任。 667 | 668 | 2、Jexus v7.0及更高版本,允许个人、20人以下的私营企业或非营利社会组织免费使用,也允许WEB项目开发厂商作为测试环境免费使用。其它情况下使用,原则上需要获得Jexus作者刘冰先生(网名:宇内流云)的商业授权。 669 | 670 | 3、Jexus作者对所有Jexus版本提供技术支持或定制开发等有偿服务。 671 | 672 | 4、为了让Jexus可持续发展,欢迎广大用户及热心朋友对作者提供力所能及的支持,包括但不限于: 673 | 674 | (1)您可以利用各种机会宣传Jexus优良品质、实用价值及成功案例,分享使用心得和技术理论,为推广、普及Jexus付出努力; 675 | 676 | (2)您可以为初学者解难答疑; 677 | 678 | (3)你可以将Jexus的一些重要的技术文档翻译为英文或其它文字并公开、免费发布,为Jexus跨出国门走向国际添砖加瓦; 679 | 680 | (4)您可以利用开发新工程或升级旧工程的机会,利用Jexus把原来部署在windows上的web应用部署到Linux上,拓展Jexus的使用量; 681 | 682 | (5)您可以通过Jexus社区、QQ群、邮箱等各种渠道及时反馈Jexus的BUG,或者向Jexus的开发者、维护者提供富有建设性意义的各种建议; 683 | 684 | (6)您可以为Jexus提供广告支持,或者向Jexus作者提供力所能及的经费赞助,为Jexus的持续发展提供动力源泉。 685 | 686 | (二)Jexus官网、社区及联系方式 687 | 688 | 官方网站:[www.jexus.org](https://www.jexus.org/ "Jexus Web Server官网") 689 | 690 | 项目仓库:[\[GitHub\]](https://github.com/yunekit/jexus)  [\[Gitee\]](https://gitee.com/yunekit/jexus) 691 | 692 | QQ群号码:700864320、102732979 693 | 694 | 邮箱地址:j66x@163.com 695 | -------------------------------------------------------------------------------- /images/jexus_logo_h32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunekit/jexus/9ef192f8d2fedea385856149b66493e157d2584e/images/jexus_logo_h32.png --------------------------------------------------------------------------------