├── .gitignore ├── .scannerwork └── .sonar_lock ├── .travis.yml ├── Dockerfile ├── HTTPServer ├── HTTPClient │ ├── App.config │ ├── HttpClient.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── bin │ │ └── Debug │ │ └── HttpClient.exe.config ├── HTTPServer.sln ├── HTTPServer │ ├── App.config │ ├── ConsoleLogger.cs │ ├── ExampleServer.cs │ ├── HttpServer.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── HTTPServerLib.UnitTest │ ├── HttpServerLib.UnitTest.csproj │ ├── HttpServerTest.cs │ ├── HttpServiceTest.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── HTTPServerLib │ ├── ActionResult.cs │ ├── BaseHeader.cs │ ├── HeadersHelper.cs │ ├── HttpRequest.cs │ ├── HttpResponse.cs │ ├── HttpServer.cs │ ├── HttpServerLib.csproj │ ├── HttpService.cs │ ├── ILogger.cs │ ├── IServer.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Protocols.cs │ ├── RequestHeaders.cs │ ├── ResponseHeaders.cs │ ├── ResponseHelper.cs │ ├── RouteAttribute.cs │ ├── RouteMethod.cs │ ├── ServiceModule.cs │ └── ServiceRoute.cs ├── Packages.dgml └── Packages │ ├── LitJson.0.7.0 │ ├── LitJson.0.7.0.nupkg │ └── lib │ │ └── LitJson.dll │ └── repositories.config ├── README.md └── sonar-project.properties /.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 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.pch 68 | *.pdb 69 | *.pgc 70 | *.pgd 71 | *.rsp 72 | *.sbr 73 | *.tlb 74 | *.tli 75 | *.tlh 76 | *.tmp 77 | *.tmp_proj 78 | *.log 79 | *.vspscc 80 | *.vssscc 81 | .builds 82 | *.pidb 83 | *.svclog 84 | *.scc 85 | 86 | # Chutzpah Test files 87 | _Chutzpah* 88 | 89 | # Visual C++ cache files 90 | ipch/ 91 | *.aps 92 | *.ncb 93 | *.opendb 94 | *.opensdf 95 | *.sdf 96 | *.cachefile 97 | *.VC.db 98 | *.VC.VC.opendb 99 | 100 | # Visual Studio profiler 101 | *.psess 102 | *.vsp 103 | *.vspx 104 | *.sap 105 | 106 | # Visual Studio Trace Files 107 | *.e2e 108 | 109 | # TFS 2012 Local Workspace 110 | $tf/ 111 | 112 | # Guidance Automation Toolkit 113 | *.gpState 114 | 115 | # ReSharper is a .NET coding add-in 116 | _ReSharper*/ 117 | *.[Rr]e[Ss]harper 118 | *.DotSettings.user 119 | 120 | # JustCode is a .NET coding add-in 121 | .JustCode 122 | 123 | # TeamCity is a build add-in 124 | _TeamCity* 125 | 126 | # DotCover is a Code Coverage Tool 127 | *.dotCover 128 | 129 | # AxoCover is a Code Coverage Tool 130 | .axoCover/* 131 | !.axoCover/settings.json 132 | 133 | # Visual Studio code coverage results 134 | *.coverage 135 | *.coveragexml 136 | 137 | # NCrunch 138 | _NCrunch_* 139 | .*crunch*.local.xml 140 | nCrunchTemp_* 141 | 142 | # MightyMoose 143 | *.mm.* 144 | AutoTest.Net/ 145 | 146 | # Web workbench (sass) 147 | .sass-cache/ 148 | 149 | # Installshield output folder 150 | [Ee]xpress/ 151 | 152 | # DocProject is a documentation generator add-in 153 | DocProject/buildhelp/ 154 | DocProject/Help/*.HxT 155 | DocProject/Help/*.HxC 156 | DocProject/Help/*.hhc 157 | DocProject/Help/*.hhk 158 | DocProject/Help/*.hhp 159 | DocProject/Help/Html2 160 | DocProject/Help/html 161 | 162 | # Click-Once directory 163 | publish/ 164 | 165 | # Publish Web Output 166 | *.[Pp]ublish.xml 167 | *.azurePubxml 168 | # Note: Comment the next line if you want to checkin your web deploy settings, 169 | # but database connection strings (with potential passwords) will be unencrypted 170 | *.pubxml 171 | *.publishproj 172 | 173 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 174 | # checkin your Azure Web App publish settings, but sensitive information contained 175 | # in these scripts will be unencrypted 176 | PublishScripts/ 177 | 178 | # NuGet Packages 179 | *.nupkg 180 | # The packages folder can be ignored because of Package Restore 181 | **/[Pp]ackages/* 182 | # except build/, which is used as an MSBuild target. 183 | !**/[Pp]ackages/build/ 184 | # Uncomment if necessary however generally it will be regenerated when needed 185 | #!**/[Pp]ackages/repositories.config 186 | # NuGet v3's project.json files produces more ignorable files 187 | *.nuget.props 188 | *.nuget.targets 189 | 190 | # Microsoft Azure Build Output 191 | csx/ 192 | *.build.csdef 193 | 194 | # Microsoft Azure Emulator 195 | ecf/ 196 | rcf/ 197 | 198 | # Windows Store app package directories and files 199 | AppPackages/ 200 | BundleArtifacts/ 201 | Package.StoreAssociation.xml 202 | _pkginfo.txt 203 | *.appx 204 | 205 | # Visual Studio cache files 206 | # files ending in .cache can be ignored 207 | *.[Cc]ache 208 | # but keep track of directories ending in .cache 209 | !*.[Cc]ache/ 210 | 211 | # Others 212 | ClientBin/ 213 | ~$* 214 | *~ 215 | *.dbmdl 216 | *.dbproj.schemaview 217 | *.jfm 218 | *.pfx 219 | *.publishsettings 220 | orleans.codegen.cs 221 | 222 | # Since there are multiple workflows, uncomment next line to ignore bower_components 223 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 224 | #bower_components/ 225 | 226 | # RIA/Silverlight projects 227 | Generated_Code/ 228 | 229 | # Backup & report files from converting an old project file 230 | # to a newer Visual Studio version. Backup files are not needed, 231 | # because we have git ;-) 232 | _UpgradeReport_Files/ 233 | Backup*/ 234 | UpgradeLog*.XML 235 | UpgradeLog*.htm 236 | 237 | # SQL Server files 238 | *.mdf 239 | *.ldf 240 | *.ndf 241 | 242 | # Business Intelligence projects 243 | *.rdl.data 244 | *.bim.layout 245 | *.bim_*.settings 246 | 247 | # Microsoft Fakes 248 | FakesAssemblies/ 249 | 250 | # GhostDoc plugin setting file 251 | *.GhostDoc.xml 252 | 253 | # Node.js Tools for Visual Studio 254 | .ntvs_analysis.dat 255 | node_modules/ 256 | 257 | # TypeScript v1 declaration files 258 | typings/ 259 | 260 | # Visual Studio 6 build log 261 | *.plg 262 | 263 | # Visual Studio 6 workspace options file 264 | *.opt 265 | 266 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 267 | *.vbw 268 | 269 | # Visual Studio LightSwitch build output 270 | **/*.HTMLClient/GeneratedArtifacts 271 | **/*.DesktopClient/GeneratedArtifacts 272 | **/*.DesktopClient/ModelManifest.xml 273 | **/*.Server/GeneratedArtifacts 274 | **/*.Server/ModelManifest.xml 275 | _Pvt_Extensions 276 | 277 | # Paket dependency manager 278 | .paket/paket.exe 279 | paket-files/ 280 | 281 | # FAKE - F# Make 282 | .fake/ 283 | 284 | # JetBrains Rider 285 | .idea/ 286 | *.sln.iml 287 | 288 | # CodeRush 289 | .cr/ 290 | 291 | # Python Tools for Visual Studio (PTVS) 292 | __pycache__/ 293 | *.pyc 294 | 295 | # Cake - Uncomment if you are using it 296 | # tools/** 297 | # !tools/packages.config 298 | 299 | # Tabs Studio 300 | *.tss 301 | 302 | # Telerik's JustMock configuration file 303 | *.jmconfig 304 | 305 | # BizTalk build output 306 | *.btp.cs 307 | *.btm.cs 308 | *.odx.cs 309 | *.xsd.cs 310 | 311 | # OpenCover UI analysis results 312 | OpenCover/ 313 | 314 | # Azure Stream Analytics local run output 315 | ASALocalRun/ 316 | sonar.bat 317 | .sonarqube 318 | -------------------------------------------------------------------------------- /.scannerwork/.sonar_lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qinyuanpei/HttpServer/f5decb7b887b3afe1b9ec55b29b8a73112851bbd/.scannerwork/.sonar_lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - oraclejdk8 3 | mono: 4 | - latest 5 | 6 | language: csharp 7 | solution: ./HTTPServer/HTTPServer.sln 8 | 9 | notifications: 10 | email: 11 | recipients: 12 | - 875974254@qq.com 13 | - qinyuanpei@163.com 14 | on_success: change # default: change 15 | on_failure: always # default: always 16 | 17 | install: 18 | - cd ./HTTPServer 19 | - nuget restore ./HTTPServer.sln # restore nuget 20 | # - nuget install NUnit.Runners -Version 3.8.0 -OutputDirectory ./TestRunner # install nunit 21 | 22 | script: 23 | - msbuild /p:Configuration=Release HTTPServer.sln 24 | # - mono ./TestRunner/NUnit.ConsoleRunner.3.8.0/tools/nunit3-console.exe ./HTTPServerLib.UnitTest/bin/Release/HttpServerLib.UnitTest.dll 25 | # - sonar-scanner -Dsonar.verbose=true -X 26 | 27 | branches: 28 | only: 29 | - master 30 | 31 | addons: 32 | sonarcloud: 33 | organization: "qinyuanpei-github" 34 | token: db795a28468dc7c12805b330afed53d362fdd2d9 35 | 36 | cache: 37 | directories: 38 | - '$HOME/.sonar/cache' 39 | 40 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | LABEL vendor="qinyuanpei@163.com" 3 | 4 | # Install Mono && XBuild 5 | RUN sudo apt-get update 6 | RUN sudo apt-get upgrade -y 7 | RUN sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF 8 | RUN sudo apt install apt-transport-https -y 9 | RUN sudo apt-get install wget -y 10 | RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-trusty main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list 11 | RUN sudo apt-get update 12 | RUN sudo apt-get install aptitude -y 13 | RUN sudo apt-get install -f 14 | RUN sudo apt-get install -y git 15 | RUN sudo apt-get install -y zip 16 | RUN sudo apt-get install -y unzip 17 | RUN sudo aptitude install -y mono-complete 18 | 19 | # Intall Nuget 20 | RUN sudo mkdir /usr/nuget/ 21 | RUN sudo wget -O /usr/nuget/nuget.exe https://dist.nuget.org/win-x86-commandline/v4.6.2/nuget.exe 22 | ENV NUGET="/usr/nuget/nuget.exe" 23 | 24 | # Install Sonar-Scanner 25 | RUN sudo wget -O sonar-scanner.zip https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.2.0.1227-linux.zip 26 | RUN sudo unzip sonar-scanner.zip -d /usr/ 27 | RUN sudo chmod -R 777 usr/sonar-scanner-3.2.0.1227-linux 28 | ENV SONAR_SCANNER="usr/sonar-scanner-3.2.0.1227-linux/bin/sonar-scanner" 29 | 30 | # Install NUnit 31 | RUN mono ${NUGET} install NUnit.Runners -Version 3.8.0 -OutputDirectory ./TestRunner 32 | ENV NUNIT="./TestRunner/NUnit.ConsoleRunner.3.8.0/tools/nunit3-console.exe" 33 | 34 | # Build Project && Sonar Analyse && UnitTest 35 | RUN sudo mkdir ./WorkSpace/ 36 | RUN cd ./WorkSpace/ 37 | RUN sudo chmod -R 777 ./WorkSpace/ 38 | RUN git clone https://github.com/qinyuanpei/HttpServer.git 39 | RUN cd ./HttpServer/ 40 | RUN sudo ${SONAR_SCANNER} -D sonar.host.url="https://sonarcloud.io" -D sonar.login="db795a28468dc7c12805b330afed53d362fdd2d9" -D sonar.projectKey="Sonar-HttpServer" -D sonar.sources="." -D sonar.projectName="HttpServer" -X 41 | RUN msbuild /p:Configuration=Release ./HTTPServer/HTTPServer.sln 42 | RUN mono ${NUNIT} ./HTTPServer/HTTPServerLib.UnitTest/bin/Release/HttpServerLib.UnitTest.dll 43 | EXPOSE 2048 44 | -------------------------------------------------------------------------------- /HTTPServer/HTTPClient/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /HTTPServer/HTTPClient/HttpClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B2A1D446-92CE-455C-9194-36C8EF225D9C} 8 | Exe 9 | Properties 10 | HttpClient 11 | HttpClient 12 | v4.6 13 | 512 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 59 | -------------------------------------------------------------------------------- /HTTPServer/HTTPClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace HttpClient 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | var request = (HttpWebRequest)WebRequest.Create("http://localhost:4050/"); 16 | request.Method = "POST"; 17 | var requestStream = request.GetRequestStream(); 18 | var data = Encoding.UTF8.GetBytes("a=10&b=15"); 19 | requestStream.Write(data, 0, data.Length); 20 | var response = request.GetResponse(); 21 | 22 | using (StreamReader reader = new StreamReader(response.GetResponseStream())) 23 | { 24 | var content = reader.ReadToEnd(); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /HTTPServer/HTTPClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("HttpClient")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HttpClient")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("71086c2e-79d1-414c-b070-c8bcb457a0e2")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HTTPServer/HTTPClient/bin/Debug/HttpClient.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpServer", "HTTPServer\HttpServer.csproj", "{E6E5FC8E-8B94-4F1E-91BB-750B40ACB96E}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpServerLib", "HTTPServerLib\HttpServerLib.csproj", "{C2310E13-8D59-4632-BF91-203BFF2FFE4F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpServerLib.UnitTest", "HTTPServerLib.UnitTest\HttpServerLib.UnitTest.csproj", "{31021B42-4303-4358-9EE2-60B1A0215AC8}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClient", "HTTPClient\HttpClient.csproj", "{B2A1D446-92CE-455C-9194-36C8EF225D9C}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E6E5FC8E-8B94-4F1E-91BB-750B40ACB96E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {E6E5FC8E-8B94-4F1E-91BB-750B40ACB96E}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {E6E5FC8E-8B94-4F1E-91BB-750B40ACB96E}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {E6E5FC8E-8B94-4F1E-91BB-750B40ACB96E}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {C2310E13-8D59-4632-BF91-203BFF2FFE4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {C2310E13-8D59-4632-BF91-203BFF2FFE4F}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {C2310E13-8D59-4632-BF91-203BFF2FFE4F}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {C2310E13-8D59-4632-BF91-203BFF2FFE4F}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {31021B42-4303-4358-9EE2-60B1A0215AC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {31021B42-4303-4358-9EE2-60B1A0215AC8}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {31021B42-4303-4358-9EE2-60B1A0215AC8}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {31021B42-4303-4358-9EE2-60B1A0215AC8}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {B2A1D446-92CE-455C-9194-36C8EF225D9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {B2A1D446-92CE-455C-9194-36C8EF225D9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {B2A1D446-92CE-455C-9194-36C8EF225D9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {B2A1D446-92CE-455C-9194-36C8EF225D9C}.Release|Any CPU.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/ConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HTTPServerLib; 7 | 8 | namespace HttpServer 9 | { 10 | public class ConsoleLogger:ILogger 11 | { 12 | public void Log(object message) 13 | { 14 | Console.WriteLine(message); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/ExampleServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HTTPServerLib; 7 | using System.IO; 8 | 9 | namespace HttpServer 10 | { 11 | public class ExampleServer : HTTPServerLib.HttpServer 12 | { 13 | /// 14 | /// 构造函数 15 | /// 16 | /// IP地址 17 | /// 端口号 18 | public ExampleServer(string ipAddress, int port) 19 | : base(ipAddress, port) 20 | { 21 | 22 | } 23 | 24 | public override void OnPost(HttpRequest request, HttpResponse response) 25 | { 26 | //获取客户端传递的参数 27 | string data = request.Params == null ? "" : string.Join(";", request.Params.Select(x => x.Key + "=" + x.Value).ToArray()); 28 | 29 | //设置返回信息 30 | string content = string.Format("这是通过Post方式返回的数据:{0}", data); 31 | 32 | //构造响应报文 33 | response.SetContent(content); 34 | response.Content_Encoding = "utf-8"; 35 | response.StatusCode = "200"; 36 | response.Content_Type = "text/html; charset=UTF-8"; 37 | response.Headers["Server"] = "ExampleServer"; 38 | 39 | //发送响应 40 | response.Send(); 41 | } 42 | 43 | public override void OnGet(HttpRequest request, HttpResponse response) 44 | { 45 | 46 | ///链接形式1:"http://localhost:4050/assets/styles/style.css"表示访问指定文件资源, 47 | ///此时读取服务器目录下的/assets/styles/style.css文件。 48 | 49 | ///链接形式1:"http://localhost:4050/assets/styles/"表示访问指定页面资源, 50 | ///此时读取服务器目录下的/assets/styles/style.index文件。 51 | 52 | //当文件不存在时应返回404状态码 53 | string requestURL = request.URL; 54 | requestURL = requestURL.Replace("/", @"\").Replace("\\..", "").TrimStart('\\'); 55 | string requestFile = Path.Combine(ServerRoot, requestURL); 56 | 57 | //判断地址中是否存在扩展名 58 | string extension = Path.GetExtension(requestFile); 59 | 60 | //根据有无扩展名按照两种不同链接进行处 61 | if (extension != "") 62 | { 63 | //从文件中返回HTTP响应 64 | response = response.FromFile(requestFile); 65 | } 66 | else 67 | { 68 | //目录存在且不存在index页面时时列举目录 69 | if (Directory.Exists(requestFile) && !File.Exists(requestFile + "\\index.html")) 70 | { 71 | requestFile = Path.Combine(ServerRoot, requestFile); 72 | var content = ListDirectory(requestFile, requestURL); 73 | response = response.SetContent(content, Encoding.UTF8); 74 | response.Content_Type = "text/html; charset=UTF-8"; 75 | } 76 | else 77 | { 78 | //加载静态HTML页面 79 | requestFile = Path.Combine(requestFile, "index.html"); 80 | response = response.FromFile(requestFile); 81 | response.Content_Type = "text/html; charset=UTF-8"; 82 | } 83 | } 84 | 85 | //发送HTTP响应 86 | response.Send(); 87 | } 88 | 89 | public override void OnDefault(HttpRequest request, HttpResponse response) 90 | { 91 | 92 | } 93 | 94 | private string ConvertPath(string[] urls) 95 | { 96 | string html = string.Empty; 97 | int length = ServerRoot.Length; 98 | foreach (var url in urls) 99 | { 100 | var s = url.StartsWith("..") ? url : url.Substring(length).TrimEnd('\\'); 101 | html += String.Format("
  • {0}
  • ", s); 102 | } 103 | 104 | return html; 105 | } 106 | 107 | private string ListDirectory(string requestDirectory, string requestURL) 108 | { 109 | //列举子目录 110 | var folders = requestURL.Length > 1 ? new string[] { "../" } : new string[] { }; 111 | folders = folders.Concat(Directory.GetDirectories(requestDirectory)).ToArray(); 112 | var foldersList = ConvertPath(folders); 113 | 114 | //列举文件 115 | var files = Directory.GetFiles(requestDirectory); 116 | var filesList = ConvertPath(files); 117 | 118 | //构造HTML 119 | StringBuilder builder = new StringBuilder(); 120 | builder.Append(string.Format("{0}", requestDirectory)); 121 | builder.Append(string.Format("

    {0}


    ", 122 | requestURL, filesList, foldersList)); 123 | 124 | return builder.ToString(); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/HttpServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E6E5FC8E-8B94-4F1E-91BB-750B40ACB96E} 8 | Exe 9 | Properties 10 | HttpServer 11 | HttpServer 12 | v4.6 13 | 512 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {c2310e13-8d59-4632-bf91-203bff2ffe4f} 57 | HttpServerLib 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HTTPServerLib; 7 | using HttpServer; 8 | 9 | namespace HTTPServerLib 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | ExampleServer server = new ExampleServer("0.0.0.0", 4050); 16 | server.SetRoot(@"D:\Hexo\public"); 17 | server.Logger = new ConsoleLogger(); 18 | server.Start(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("HTTPServer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HTTPServer")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("c0256988-62e5-447f-b056-09288e77950e")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib.UnitTest/HttpServerLib.UnitTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {31021B42-4303-4358-9EE2-60B1A0215AC8} 7 | Library 8 | Properties 9 | HttpServerLib.UnitTest 10 | HttpServerLib.UnitTest 11 | v4.6 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | ..\packages\NUnit.3.8.0\lib\net45\nunit.framework.dll 41 | True 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | False 66 | 67 | 68 | False 69 | 70 | 71 | False 72 | 73 | 74 | False 75 | 76 | 77 | 78 | 79 | 80 | 81 | 88 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib.UnitTest/HttpServerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using System.Net; 4 | using System.Text; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace HTTPServerLib.UnitTest 11 | { 12 | [TestFixture] 13 | public class HttpServerTest 14 | { 15 | [Test] 16 | public void TestGet() 17 | { 18 | var request = (HttpWebRequest)WebRequest.Create("http://localhost:4050/"); 19 | var response = request.GetResponse(); 20 | Assert.AreEqual("text/html; charset=UTF-8", response.Headers["Content-Type"]); 21 | Assert.AreEqual("ExampleServer", response.Headers["Server"]); 22 | } 23 | 24 | [Test] 25 | public void TestPost() 26 | { 27 | var request = (HttpWebRequest)WebRequest.Create("http://localhost:4050/"); 28 | request.Method = "POST"; 29 | var requestStream = request.GetRequestStream(); 30 | var data = Encoding.UTF8.GetBytes("a=10&b=15"); 31 | requestStream.Write(data,0,data.Length); 32 | var response = request.GetResponse(); 33 | Assert.AreEqual("text/html; charset=UTF-8", response.Headers["Content-Type"]); 34 | Assert.AreEqual("ExampleServer", response.Headers["Server"]); 35 | using (StreamReader reader = new StreamReader(response.GetResponseStream())) 36 | { 37 | var content = reader.ReadToEnd(); 38 | Assert.AreEqual("这是通过Post方式返回的数据:a=10;b=15", content); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib.UnitTest/HttpServiceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using NUnit.Framework; 5 | 6 | namespace HttpServerLib.UnitTest 7 | { 8 | [TestFixture] 9 | public class HttpServiceTest 10 | { 11 | 12 | [Test] 13 | public void TestGet() 14 | { 15 | 16 | } 17 | 18 | [Test] 19 | public void TestPost() 20 | { 21 | 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib.UnitTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下特性集 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("HTTPServerLib.UnitTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HTTPServerLib.UnitTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 19 | // 请将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("c7d7cd2e-b24f-4761-9562-6ea8edd8287c")] 24 | 25 | // 程序集的版本信息由以下四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib.UnitTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/ActionResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public class ActionResult 10 | { 11 | public object Result { get; set; } 12 | public bool Success { get; set; } 13 | public string Message { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/BaseHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public class BaseHeader 10 | { 11 | public string Body { get; set; } 12 | 13 | public Encoding Encoding { get; set; } 14 | 15 | public string Content_Type { get; set; } 16 | 17 | public string Content_Length { get; set; } 18 | 19 | public string Content_Encoding { get; set; } 20 | 21 | public string ContentLanguage { get; set; } 22 | 23 | public Dictionary Headers { get; set; } 24 | 25 | /// 26 | /// 不支持枚举类型约束,所以采取下列方案:) 27 | /// 28 | protected string GetHeaderByKey(Enum header) 29 | { 30 | var fieldName = header.GetDescription(); 31 | if (fieldName == null) return null; 32 | var hasKey = Headers.ContainsKey(fieldName); 33 | if (!hasKey) return null; 34 | return Headers[fieldName]; 35 | } 36 | 37 | protected string GetHeaderByKey(string fieldName) 38 | { 39 | if (string.IsNullOrEmpty(fieldName)) return null; 40 | var hasKey = Headers.ContainsKey(fieldName); 41 | if (!hasKey) return null; 42 | return Headers[fieldName]; 43 | } 44 | 45 | /// 46 | /// 不支持枚举类型约束,所以采取下列方案:) 47 | /// 48 | protected void SetHeaderByKey(Enum header, string value) 49 | { 50 | var fieldName = header.GetDescription(); 51 | if (fieldName == null) return; 52 | var hasKey = Headers.ContainsKey(fieldName); 53 | if (!hasKey) Headers.Add(fieldName, value); 54 | Headers[fieldName] = value; 55 | } 56 | 57 | protected void SetHeaderByKey(string fieldName, string value) 58 | { 59 | if (string.IsNullOrEmpty(fieldName)) return; 60 | var hasKey = Headers.ContainsKey(fieldName); 61 | if (!hasKey) Headers.Add(fieldName, value); 62 | Headers[fieldName] = value; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/HeadersHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace HTTPServerLib 9 | { 10 | public static class HeadersHelper 11 | { 12 | public static string GetDescription(this Enum value) 13 | { 14 | var valueType = value.GetType(); 15 | var memberName = Enum.GetName(valueType, value); 16 | if (memberName == null) return null; 17 | var fieldInfo = valueType.GetField(memberName); 18 | var attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)); 19 | if (attribute == null) return null; 20 | return (attribute as DescriptionAttribute).Description; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Net.Sockets; 7 | using System.IO; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace HTTPServerLib 11 | { 12 | /// 13 | /// HTTP请求定义 14 | /// 15 | public class HttpRequest : BaseHeader 16 | { 17 | /// 18 | /// URL参数 19 | /// 20 | public Dictionary Params { get; private set; } 21 | 22 | /// 23 | /// HTTP请求方式 24 | /// 25 | public string Method { get; private set; } 26 | 27 | /// 28 | /// HTTP(S)地址 29 | /// 30 | public string URL { get; set; } 31 | 32 | /// 33 | /// HTTP协议版本 34 | /// 35 | public string ProtocolVersion { get; set; } 36 | 37 | /// 38 | /// 定义缓冲区 39 | /// 40 | private const int MAX_SIZE = 1024 * 1024 * 2; 41 | private byte[] bytes = new byte[MAX_SIZE]; 42 | 43 | public ILogger Logger { get; set; } 44 | 45 | private Stream handler; 46 | 47 | public HttpRequest(Stream stream) 48 | { 49 | this.handler = stream; 50 | var data = GetRequestData(handler); 51 | var rows = Regex.Split(data, Environment.NewLine); 52 | 53 | //Request URL & Method & Version 54 | var first = Regex.Split(rows[0], @"(\s+)") 55 | .Where(e => e.Trim() != string.Empty) 56 | .ToArray(); 57 | if (first.Length > 0) this.Method = first[0]; 58 | if (first.Length > 1) this.URL = Uri.UnescapeDataString(first[1]); 59 | if (first.Length > 2) this.ProtocolVersion = first[2]; 60 | 61 | //Request Headers 62 | this.Headers = GetRequestHeaders(rows); 63 | 64 | //Request Body 65 | Body = GetRequestBody(rows); 66 | var contentLength = GetHeader(RequestHeaders.ContentLength); 67 | if (int.TryParse(contentLength, out var length) && Body.Length != length) 68 | { 69 | do 70 | { 71 | length = stream.Read(bytes, 0, MAX_SIZE - 1); 72 | Body += Encoding.UTF8.GetString(bytes, 0, length); 73 | } while (Body.Length != length); 74 | } 75 | 76 | //Request "GET" 77 | if (this.Method == "GET") 78 | { 79 | var isUrlencoded = this.URL.Contains('?'); 80 | if (isUrlencoded) this.Params = GetRequestParameters(URL.Split('?')[1]); 81 | } 82 | 83 | //Request "POST" 84 | if (this.Method == "POST") 85 | { 86 | var contentType = GetHeader(RequestHeaders.ContentType); 87 | var isUrlencoded = contentType == @"application/x-www-form-urlencoded"; 88 | if (isUrlencoded) this.Params = GetRequestParameters(this.Body); 89 | } 90 | } 91 | 92 | public Stream GetRequestStream() 93 | { 94 | return this.handler; 95 | } 96 | 97 | public string GetHeader(RequestHeaders header) 98 | { 99 | return GetHeaderByKey(header); 100 | } 101 | 102 | public string GetHeader(string fieldName) 103 | { 104 | return GetHeaderByKey(fieldName); 105 | } 106 | 107 | public void SetHeader(RequestHeaders header, string value) 108 | { 109 | SetHeaderByKey(header, value); 110 | } 111 | 112 | public void SetHeader(string fieldName,string value) 113 | { 114 | SetHeaderByKey(fieldName, value); 115 | } 116 | 117 | private string GetRequestData(Stream stream) 118 | { 119 | var length = 0; 120 | var data = string.Empty; 121 | 122 | do 123 | { 124 | length = stream.Read(bytes, 0, MAX_SIZE - 1); 125 | data += Encoding.UTF8.GetString(bytes, 0, length); 126 | } while (length > 0 && !data.Contains("\r\n\r\n")); 127 | 128 | return data; 129 | } 130 | 131 | private string GetRequestBody(IEnumerable rows) 132 | { 133 | var target = rows.Select((v, i) => new { Value = v, Index = i }).FirstOrDefault(e => e.Value.Trim() == string.Empty); 134 | if (target == null) return null; 135 | var range = Enumerable.Range(target.Index + 1, rows.Count() - target.Index - 1); 136 | return string.Join(Environment.NewLine, range.Select(e => rows.ElementAt(e)).ToArray()); 137 | } 138 | 139 | private Dictionary GetRequestHeaders(IEnumerable rows) 140 | { 141 | if (rows == null || rows.Count() <= 0) return null; 142 | var target = rows.Select((v, i) => new { Value = v, Index = i }).FirstOrDefault(e => e.Value.Trim() == string.Empty); 143 | var length = target == null ? rows.Count() - 1 : target.Index; 144 | if (length <= 1) return null; 145 | var range = Enumerable.Range(1, length - 1); 146 | return range.Select(e => rows.ElementAt(e)).ToDictionary(e => e.Split(':')[0], e => e.Split(':')[1].Trim()); 147 | } 148 | 149 | private Dictionary GetRequestParameters(string row) 150 | { 151 | if (string.IsNullOrEmpty(row)) return null; 152 | var kvs = Regex.Split(row, "&"); 153 | if (kvs == null || kvs.Count() <= 0) return null; 154 | 155 | return kvs.ToDictionary(e => Regex.Split(e, "=")[0], e => { var p = Regex.Split(e, "="); return p.Length > 1 ? p[1] : ""; }); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/HttpResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace HTTPServerLib 9 | { 10 | public class HttpResponse : BaseHeader 11 | { 12 | public string StatusCode { get; set; } 13 | 14 | public string Protocols { get; set; } 15 | 16 | public string ProtocolsVersion { get; set; } 17 | 18 | public byte[] Content { get; private set; } 19 | 20 | private Stream handler; 21 | 22 | public ILogger Logger { get; set; } 23 | 24 | public HttpResponse(Stream stream) 25 | { 26 | this.handler = stream; 27 | this.Headers = new Dictionary(); 28 | } 29 | 30 | public HttpResponse SetContent(byte[] content, Encoding encoding = null) 31 | { 32 | this.Content = content; 33 | this.Encoding = encoding != null ? encoding : Encoding.UTF8; 34 | this.Content_Length = content.Length.ToString(); 35 | return this; 36 | } 37 | 38 | public HttpResponse SetContent(string content, Encoding encoding = null) 39 | { 40 | //初始化内容 41 | encoding = encoding != null ? encoding : Encoding.UTF8; 42 | return SetContent(encoding.GetBytes(content), encoding); 43 | } 44 | 45 | public Stream GetResponseStream() 46 | { 47 | return this.handler; 48 | } 49 | 50 | public string GetHeader(ResponseHeaders header) 51 | { 52 | return GetHeaderByKey(header); 53 | } 54 | 55 | public string GetHeader(string fieldName) 56 | { 57 | return GetHeaderByKey(fieldName); 58 | } 59 | 60 | public void SetHeader(ResponseHeaders header, string value) 61 | { 62 | SetHeaderByKey(header, value); 63 | } 64 | 65 | public void SetHeader(string fieldName, string value) 66 | { 67 | SetHeaderByKey(fieldName, value); 68 | } 69 | 70 | /// 71 | /// 构建响应头部 72 | /// 73 | /// 74 | protected string BuildHeader() 75 | { 76 | StringBuilder builder = new StringBuilder(); 77 | 78 | if (!string.IsNullOrEmpty(StatusCode)) 79 | builder.Append("HTTP/1.1 " + StatusCode + "\r\n"); 80 | 81 | if (!string.IsNullOrEmpty(this.Content_Type)) 82 | builder.AppendLine("Content-Type:" + this.Content_Type); 83 | return builder.ToString(); 84 | } 85 | 86 | /// 87 | /// 发送数据 88 | /// 89 | public void Send() 90 | { 91 | if (!handler.CanWrite) return; 92 | 93 | try 94 | { 95 | //发送响应头 96 | var header = BuildHeader(); 97 | byte[] headerBytes = this.Encoding.GetBytes(header); 98 | handler.Write(headerBytes, 0, headerBytes.Length); 99 | 100 | //发送空行 101 | byte[] lineBytes = this.Encoding.GetBytes(System.Environment.NewLine); 102 | handler.Write(lineBytes, 0, lineBytes.Length); 103 | 104 | //发送内容 105 | handler.Write(Content, 0, Content.Length); 106 | } 107 | catch (Exception e) 108 | { 109 | Log(e.Message); 110 | } 111 | finally 112 | { 113 | handler.Close(); 114 | } 115 | } 116 | 117 | /// 118 | /// 记录日志 119 | /// 120 | /// 日志消息 121 | private void Log(object message) 122 | { 123 | if (Logger != null) 124 | Logger.Log(message); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/HttpServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.Security; 5 | using System.Net.Sockets; 6 | using System.Security.Authentication; 7 | using System.Security.Cryptography.X509Certificates; 8 | using System.Threading; 9 | using System.Runtime.InteropServices; 10 | 11 | namespace HTTPServerLib 12 | { 13 | 14 | public class HttpServer : IServer 15 | { 16 | /// 17 | /// 服务器IP 18 | /// 19 | public string ServerIP { get; private set; } 20 | 21 | /// 22 | /// 服务器端口 23 | /// 24 | public int ServerPort { get; private set; } 25 | 26 | /// 27 | /// 服务器目录 28 | /// 29 | public string ServerRoot { get; private set; } 30 | 31 | /// 32 | /// 是否运行 33 | /// 34 | public bool IsRunning { get; private set; } 35 | 36 | /// 37 | /// 服务器协议 38 | /// 39 | public Protocols Protocol { get; private set; } 40 | 41 | /// 42 | /// 服务端Socet 43 | /// 44 | private TcpListener serverListener; 45 | 46 | /// 47 | /// 日志接口 48 | /// 49 | public ILogger Logger { get; set; } 50 | 51 | /// 52 | /// SSL证书 53 | /// 54 | private X509Certificate serverCertificate = null; 55 | 56 | /// 57 | /// 构造函数 58 | /// 59 | /// IP地址 60 | /// 端口号 61 | /// 根目录 62 | private HttpServer(IPAddress ipAddress, int port, string root) 63 | { 64 | this.ServerIP = ipAddress.ToString(); 65 | this.ServerPort = port; 66 | 67 | //如果指定目录不存在则采用默认目录 68 | if (!Directory.Exists(root)) 69 | this.ServerRoot = AppDomain.CurrentDomain.BaseDirectory; 70 | 71 | this.ServerRoot = root; 72 | } 73 | 74 | /// 75 | /// 构造函数 76 | /// 77 | /// IP地址 78 | /// 端口号 79 | /// 根目录 80 | public HttpServer(string ipAddress, int port, string root) : 81 | this(IPAddress.Parse(ipAddress), port, root) 82 | { } 83 | 84 | /// 85 | /// 构造函数 86 | /// 87 | /// IP地址 88 | /// 端口号 89 | public HttpServer(string ipAddress, int port) : 90 | this(IPAddress.Parse(ipAddress), port, AppDomain.CurrentDomain.BaseDirectory) 91 | { } 92 | 93 | 94 | /// 95 | /// 构造函数 96 | /// 97 | /// 端口号 98 | /// 根目录 99 | public HttpServer(int port, string root) : 100 | this(IPAddress.Loopback, port, root) 101 | { } 102 | 103 | /// 104 | /// 构造函数 105 | /// 106 | /// 端口号 107 | public HttpServer(int port) : 108 | this(IPAddress.Loopback, port, AppDomain.CurrentDomain.BaseDirectory) 109 | { } 110 | 111 | 112 | /// 113 | /// 构造函数 114 | /// 115 | /// 116 | public HttpServer(string ip) : 117 | this(IPAddress.Parse(ip), 80, AppDomain.CurrentDomain.BaseDirectory) 118 | { } 119 | 120 | #region 公开方法 121 | 122 | /// 123 | /// 开启服务器 124 | /// 125 | public void Start() 126 | { 127 | if (IsRunning) return; 128 | 129 | //创建服务端Socket 130 | this.serverListener = new TcpListener(IPAddress.Parse(ServerIP), ServerPort); 131 | this.Protocol = serverCertificate == null ? Protocols.Http : Protocols.Https; 132 | this.IsRunning = true; 133 | this.serverListener.Start(); 134 | this.Log(string.Format("Sever is running at {0}://{1}:{2}", Protocol.ToString().ToLower(), ServerIP, ServerPort)); 135 | 136 | try 137 | { 138 | while (IsRunning) 139 | { 140 | TcpClient client = serverListener.AcceptTcpClient(); 141 | Thread requestThread = new Thread(() => { ProcessRequest(client); }); 142 | requestThread.Start(); 143 | } 144 | } 145 | catch (Exception e) 146 | { 147 | Log(e.Message); 148 | } 149 | } 150 | 151 | 152 | public HttpServer SetSSL(string certificate) 153 | { 154 | return SetSSL(X509Certificate.CreateFromCertFile(certificate)); 155 | } 156 | 157 | 158 | public HttpServer SetSSL(X509Certificate certifiate) 159 | { 160 | this.serverCertificate = certifiate; 161 | return this; 162 | } 163 | 164 | public void Stop() 165 | { 166 | if (!IsRunning) return; 167 | 168 | IsRunning = false; 169 | serverListener.Stop(); 170 | } 171 | 172 | /// 173 | /// 设置服务器目录 174 | /// 175 | /// 176 | public HttpServer SetRoot(string root) 177 | { 178 | if (!Directory.Exists(root)) 179 | this.ServerRoot = AppDomain.CurrentDomain.BaseDirectory; 180 | 181 | this.ServerRoot = root; 182 | return this; 183 | } 184 | /// 185 | /// 获取服务器目录 186 | /// 187 | public string GetRoot() 188 | { 189 | return this.ServerRoot; 190 | } 191 | 192 | /// 193 | /// 设置端口 194 | /// 195 | /// 端口号 196 | /// 197 | public HttpServer SetPort(int port) 198 | { 199 | this.ServerPort = port; 200 | return this; 201 | } 202 | 203 | 204 | #endregion 205 | 206 | #region 内部方法 207 | 208 | /// 209 | /// 处理客户端请求 210 | /// 211 | /// 客户端Socket 212 | private void ProcessRequest(TcpClient handler) 213 | { 214 | //处理请求 215 | Stream clientStream = handler.GetStream(); 216 | 217 | //处理SSL 218 | if (serverCertificate != null) clientStream = ProcessSSL(clientStream); 219 | if (clientStream == null) return; 220 | 221 | //构造HTTP请求 222 | HttpRequest request = new HttpRequest(clientStream); 223 | request.Logger = Logger; 224 | 225 | //构造HTTP响应 226 | HttpResponse response = new HttpResponse(clientStream); 227 | response.Logger = Logger; 228 | 229 | //处理请求类型 230 | switch (request.Method) 231 | { 232 | case "GET": 233 | OnGet(request, response); 234 | break; 235 | case "POST": 236 | OnPost(request, response); 237 | break; 238 | default: 239 | OnDefault(request, response); 240 | break; 241 | } 242 | } 243 | 244 | 245 | /// 246 | /// 处理ssl加密请求 247 | /// 248 | /// 249 | /// 250 | private Stream ProcessSSL(Stream clientStream) 251 | { 252 | try 253 | { 254 | SslStream sslStream = new SslStream(clientStream); 255 | sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true); 256 | sslStream.ReadTimeout = 10000; 257 | sslStream.WriteTimeout = 10000; 258 | return sslStream; 259 | } 260 | catch (Exception e) 261 | { 262 | Log(e.Message); 263 | clientStream.Close(); 264 | } 265 | 266 | return null; 267 | } 268 | 269 | /// 270 | /// 记录日志 271 | /// 272 | /// 日志消息 273 | protected void Log(object message) 274 | { 275 | if (Logger != null) Logger.Log(message); 276 | } 277 | 278 | #endregion 279 | 280 | #region 虚方法 281 | 282 | /// 283 | /// 响应Get请求 284 | /// 285 | /// 请求报文 286 | public virtual void OnGet(HttpRequest request, HttpResponse response) 287 | { 288 | 289 | } 290 | 291 | /// 292 | /// 响应Post请求 293 | /// 294 | /// 295 | public virtual void OnPost(HttpRequest request, HttpResponse response) 296 | { 297 | 298 | } 299 | 300 | /// 301 | /// 响应默认请求 302 | /// 303 | 304 | public virtual void OnDefault(HttpRequest request, HttpResponse response) 305 | { 306 | 307 | } 308 | 309 | #endregion 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/HttpServerLib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C2310E13-8D59-4632-BF91-203BFF2FFE4F} 8 | Library 9 | Properties 10 | HTTPServerLib 11 | HttpServerLib 12 | v4.6 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/HttpService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public class HttpService : HttpServer 10 | { 11 | private List m_modules; 12 | 13 | public HttpService(string ipAddress, int port) 14 | : base(ipAddress, port) 15 | { 16 | m_modules = new List(); 17 | } 18 | 19 | /// 20 | /// 注册模块 21 | /// 22 | /// ServiceModule 23 | public void RegisterModule(ServiceModule module) 24 | { 25 | this.m_modules.Add(module); 26 | } 27 | 28 | /// 29 | /// 卸载模块 30 | /// 31 | /// 32 | public void RemoveModule(ServiceModule module) 33 | { 34 | this.m_modules.Remove(module); 35 | } 36 | 37 | public override void OnDefault(HttpRequest request, HttpResponse response) 38 | { 39 | base.OnDefault(request, response); 40 | } 41 | 42 | public override void OnGet(HttpRequest request, HttpResponse response) 43 | { 44 | ServiceRoute route = ServiceRoute.Parse(request); 45 | ServiceModule module = m_modules.FirstOrDefault(m => m.SearchRoute(route)); 46 | if (module != null){ 47 | var result = module.ExecuteRoute(route); 48 | } 49 | } 50 | 51 | public override void OnPost(HttpRequest request, HttpResponse response) 52 | { 53 | base.OnPost(request, response); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/ILogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public interface ILogger 10 | { 11 | void Log(object message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/IServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public interface IServer 10 | { 11 | /// 12 | /// 响应GET方法 13 | /// 14 | /// Http请求 15 | void OnGet(HttpRequest request, HttpResponse response); 16 | 17 | 18 | /// 19 | /// 响应Post方法 20 | /// 21 | /// Http请求 22 | void OnPost(HttpRequest request, HttpResponse response); 23 | 24 | 25 | /// 26 | /// 响应默认请求 27 | /// 28 | /// Http请求 29 | void OnDefault(HttpRequest request, HttpResponse response); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("HttpServerLib")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HttpServerLib")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("ed455757-2cb9-4206-bb94-0db98ce9c917")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/Protocols.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public enum Protocols 10 | { 11 | Http = 0, 12 | 13 | Https = 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/RequestHeaders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace HTTPServerLib 9 | { 10 | public enum RequestHeaders 11 | { 12 | /// 13 | /// Cache-Control 标头,指定请求/响应链上所有缓存控制机制必须服从的指令。 14 | /// 15 | [Description("Cache-Control")] 16 | CacheControl = 0, 17 | 18 | /// 19 | /// Connection 标头,指定特定连接需要的选项。 20 | /// 21 | [Description("Connection")] 22 | Connection = 1, 23 | 24 | /// 25 | /// Date 标头,指定开始创建请求的日期和时间。 26 | /// 27 | [Description("Date")] 28 | Date = 2, 29 | 30 | /// 31 | /// Keep-Alive 标头,指定用以维护持久性连接的参数。 32 | /// 33 | [Description("Keep-Alive")] 34 | KeepAlive = 3, 35 | 36 | /// 37 | /// Pragma 标头,指定可应用于请求/响应链上的任何代理的特定于实现的指令。 38 | /// 39 | [Description("Pragma")] 40 | Pragma = 4, 41 | 42 | /// 43 | /// Trailer 标头,指定标头字段显示在以 chunked 传输编码方式编码的消息的尾部。 44 | /// 45 | [Description("Trailer")] 46 | Trailer = 5, 47 | 48 | /// 49 | /// Transfer-Encoding 标头,指定对消息正文应用的转换的类型(如果有)。 50 | /// 51 | [Description("Transfer-Encoding")] 52 | TransferEncoding = 6, 53 | 54 | /// 55 | /// Upgrade 标头,指定客户端支持的附加通信协议。 56 | /// 57 | [Description("Upgrade")] 58 | Upgrade = 7, 59 | 60 | /// 61 | /// Via 标头,指定网关和代理程序要使用的中间协议。 62 | /// 63 | [Description("Via")] 64 | Via = 8, 65 | 66 | /// 67 | /// Warning 标头,指定关于可能未在消息中反映的消息的状态或转换的附加信息。 68 | /// 69 | [Description("Warning")] 70 | Warning = 9, 71 | 72 | /// 73 | /// Allow 标头,指定支持的 HTTP 方法集。 74 | /// 75 | [Description("Allow")] 76 | Allow = 10, 77 | 78 | /// 79 | /// Content-Length 标头,指定伴随正文数据的长度(以字节为单位)。 80 | /// 81 | [Description("Content-Length")] 82 | ContentLength = 11, 83 | 84 | /// 85 | /// Content-Type 标头,指定伴随正文数据的 MIME 类型。 86 | /// 87 | [Description("Content-Type")] 88 | ContentType = 12, 89 | 90 | /// 91 | /// Content-Encoding 标头,指定已应用于伴随正文数据的编码。 92 | /// 93 | [Description("Content-Encoding")] 94 | ContentEncoding = 13, 95 | 96 | /// 97 | /// Content-Langauge 标头,指定伴随正文数据的自然语言。 98 | /// 99 | [Description("Content-Langauge")] 100 | ContentLanguage = 14, 101 | 102 | /// 103 | /// Content-Location 标头,指定可从其中获得伴随正文的 URI。 104 | /// 105 | [Description("Content-Location")] 106 | ContentLocation = 15, 107 | 108 | /// 109 | /// Content-MD5 标头,指定伴随正文数据的 MD5 摘要,用于提供端到端消息完整性检查。 110 | /// 111 | [Description("Content-MD5")] 112 | ContentMd5 = 16, 113 | 114 | /// 115 | /// Content-Range 标头,指定在完整正文中应用伴随部分正文数据的位置。 116 | /// 117 | [Description("Content-Range")] 118 | ContentRange = 17, 119 | 120 | /// 121 | /// Expires 标头,指定日期和时间,在此之后伴随的正文数据应视为陈旧的。 122 | /// 123 | [Description("Expires")] 124 | Expires = 18, 125 | 126 | /// 127 | /// Last-Modified 标头,指定上次修改伴随的正文数据的日期和时间。 128 | /// 129 | [Description("Last-Modified")] 130 | LastModified = 19, 131 | 132 | /// 133 | /// Accept 标头,指定响应可接受的 MIME 类型。 134 | /// 135 | [Description("Accept")] 136 | Accept = 20, 137 | 138 | /// 139 | /// Accept-Charset 标头,指定响应可接受的字符集。 140 | /// 141 | [Description("Accept-Charset")] 142 | AcceptCharset = 21, 143 | 144 | /// 145 | /// Accept-Encoding 标头,指定响应可接受的内容编码。 146 | /// 147 | [Description("Accept-Encoding")] 148 | AcceptEncoding = 22, 149 | 150 | /// 151 | /// Accept-Langauge 标头,指定响应首选的自然语言。 152 | /// 153 | [Description("Accept-Langauge")] 154 | AcceptLanguage = 23, 155 | 156 | /// 157 | /// Authorization 标头,指定客户端为向服务器验证自身身份而出示的凭据。 158 | /// 159 | [Description("Authorization")] 160 | Authorization = 24, 161 | 162 | /// 163 | /// Cookie 标头,指定向服务器提供的 Cookie 数据。 164 | /// 165 | [Description("Cookie")] 166 | Cookie = 25, 167 | 168 | /// 169 | /// Expect 标头,指定客户端要求的特定服务器行为。 170 | /// 171 | [Description("Expect")] 172 | Expect = 26, 173 | 174 | /// 175 | /// From 标头,指定控制请求用户代理的用户的 Internet 电子邮件地址。 176 | /// 177 | [Description("From")] 178 | From = 27, 179 | 180 | /// 181 | /// Host 标头,指定所请求资源的主机名和端口号。 182 | /// 183 | [Description("Host")] 184 | Host = 28, 185 | 186 | /// 187 | /// If-Match 标头,指定仅当客户端的指示资源的缓存副本是最新的时,才执行请求的操作。 188 | /// 189 | [Description("If-Match")] 190 | IfMatch = 29, 191 | 192 | /// 193 | /// If-Modified-Since 标头,指定仅当自指示的数据和时间之后修改了请求的资源时,才执行请求的操作。 194 | /// 195 | [Description("If-Modified-Since")] 196 | IfModifiedSince = 30, 197 | 198 | /// 199 | /// If-None-Match 标头,指定仅当客户端的指示资源的缓存副本都不是最新的时,才执行请求的操作。 200 | /// 201 | [Description("If-None-Match")] 202 | IfNoneMatch = 31, 203 | 204 | /// 205 | /// If-Range 标头,指定如果客户端的缓存副本是最新的,仅发送指定范围的请求资源。 206 | /// 207 | [Description("If-Range")] 208 | IfRange = 32, 209 | 210 | /// 211 | /// If-Unmodified-Since 标头,指定仅当自指示的日期和时间之后修改了请求的资源时,才执行请求的操作。 212 | /// 213 | [Description("If-Unmodified-Since")] 214 | IfUnmodifiedSince = 33, 215 | 216 | /// 217 | /// Max-Forwards 标头,指定一个整数,表示此请求还可转发的次数。 218 | /// 219 | [Description("Max-Forwards")] 220 | MaxForwards = 34, 221 | 222 | /// 223 | /// Proxy-Authorization 标头,指定客户端为向代理验证自身身份而出示的凭据。 224 | /// 225 | [Description("Proxy-Authorization")] 226 | ProxyAuthorization = 35, 227 | 228 | /// 229 | /// Referer 标头,指定从中获得请求 URI 的资源的 URI。 230 | /// 231 | [Description("Referer")] 232 | Referer = 36, 233 | 234 | /// 235 | /// Range 标头,指定代替整个响应返回的客户端请求的响应的子范围。 236 | /// 237 | [Description("Range")] 238 | Range = 37, 239 | 240 | /// 241 | /// TE 标头,指定响应可接受的传输编码方式。 242 | /// 243 | [Description("TE")] 244 | Te = 38, 245 | 246 | /// 247 | /// Translate 标头,与 WebDAV 功能一起使用的 HTTP 规范的 Microsoft 扩展。 248 | /// 249 | [Description("Translate")] 250 | Translate = 39, 251 | 252 | /// 253 | /// User-Agent 标头,指定有关客户端代理的信息。 254 | /// 255 | [Description("User-Agent")] 256 | UserAgent = 40, 257 | 258 | 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/ResponseHeaders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace HTTPServerLib 9 | { 10 | public enum ResponseHeaders 11 | { 12 | /// 13 | /// Cache-Control 标头,指定请求/响应链上所有缓存机制必须服从的缓存指令。 14 | /// 15 | [Description("Cache-Control")] 16 | CacheControl = 0, 17 | 18 | /// 19 | /// Connection 标头,指定特定连接需要的选项。 20 | /// 21 | [Description("Connection")] 22 | Connection = 1, 23 | 24 | /// 25 | /// Date 标头,指定响应产生的日期和时间。 26 | /// 27 | [Description("Date")] 28 | Date = 2, 29 | 30 | /// 31 | /// Keep-Alive 标头,指定用于维护持久连接的参数。 32 | /// 33 | [Description("Keep-Alive")] 34 | KeepAlive = 3, 35 | 36 | /// 37 | /// Pragma 标头,指定可应用于请求/响应链上的任何代理的特定于实现的指令。 38 | /// 39 | [Description("Pragma")] 40 | Pragma = 4, 41 | 42 | /// 43 | /// Trailer 标头,指定指示的标头字段在消息(使用分块传输编码方法进行编码)的尾部显示。 44 | /// 45 | [Description("Trailer")] 46 | Trailer = 5, 47 | 48 | /// 49 | /// Transfer-Encoding 标头,指定对消息正文应用哪种类型的转换(如果有)。 50 | /// 51 | [Description("Transfer-Encoding")] 52 | TransferEncoding = 6, 53 | 54 | /// 55 | /// Upgrade 标头,指定客户端支持的附加通信协议。 56 | /// 57 | [Description("Upgrade")] 58 | Upgrade = 7, 59 | 60 | /// 61 | /// Via 标头,指定网关和代理程序要使用的中间协议。 62 | /// 63 | [Description("Via")] 64 | Via = 8, 65 | 66 | /// 67 | /// Warning 标头,指定关于可能未在消息中反映的消息的状态或转换的附加信息。 68 | /// 69 | [Description("Warning")] 70 | Warning = 9, 71 | 72 | /// 73 | /// Allow 标头,指定支持的 HTTP 方法集。 74 | /// 75 | [Description("Allow")] 76 | Allow = 10, 77 | 78 | /// 79 | /// Content-Length 标头,指定伴随正文数据的长度(以字节为单位)。 80 | /// 81 | [Description("Content-Length")] 82 | ContentLength = 11, 83 | 84 | /// 85 | /// Content-Type 标头,指定伴随正文数据的 MIME 类型。 86 | /// 87 | [Description("Content-Type")] 88 | ContentType = 12, 89 | 90 | /// 91 | /// Content-Encoding 标头,指定已应用于伴随正文数据的编码。 92 | /// 93 | [Description("Content-Encoding")] 94 | ContentEncoding = 13, 95 | 96 | /// 97 | /// Content-Langauge 标头,指定自然语言或伴随正文数据的语言。 98 | /// 99 | [Description("Content-Langauge")] 100 | ContentLanguage = 14, 101 | 102 | /// 103 | /// Content-Location 标头,指定可以从中获取伴随正文的 URI。 104 | /// 105 | [Description("Content-Location")] 106 | ContentLocation = 15, 107 | 108 | /// 109 | /// Content-MD5 标头,指定伴随正文数据的 MD5 摘要,用于提供端到端消息完整性检查。 110 | /// 111 | [Description("Content-MD5")] 112 | ContentMd5 = 16, 113 | 114 | /// 115 | /// Range 标头,指定客户端请求返回的响应的单个或多个子范围来代替整个响应。 116 | /// 117 | [Description("Range")] 118 | ContentRange = 17, 119 | 120 | /// 121 | /// Expires 标头,指定日期和时间,在此之后伴随的正文数据应视为陈旧的。 122 | /// 123 | [Description("Expires")] 124 | Expires = 18, 125 | 126 | /// 127 | /// Last-Modified 标头,指定上次修改伴随的正文数据的日期和时间。 128 | /// 129 | [Description("Last-Modified")] 130 | LastModified = 19, 131 | 132 | /// 133 | /// Accept-Ranges 标头,指定服务器接受的范围。 134 | /// 135 | [Description("Accept-Ranges")] 136 | AcceptRanges = 20, 137 | 138 | /// 139 | /// Age 标头,指定自起始服务器生成响应以来的时间长度(以秒为单位)。 140 | /// 141 | [Description("Age")] 142 | Age = 21, 143 | 144 | /// 145 | /// Etag 标头,指定请求的变量的当前值。 146 | /// 147 | [Description("Etag")] 148 | ETag = 22, 149 | 150 | /// 151 | /// Location 标头,指定为获取请求的资源而将客户端重定向到的 URI。 152 | /// 153 | [Description("Location")] 154 | Location = 23, 155 | 156 | /// 157 | /// Proxy-Authenticate 标头,指定客户端必须对代理验证其自身。 158 | /// 159 | [Description("Proxy-Authenticate")] 160 | ProxyAuthenticate = 24, 161 | 162 | /// 163 | /// Retry-After 标头,指定某个时间(以秒为单位)或日期和时间,在此时间之后客户端可以重试其请求。 164 | /// 165 | [Description("Retry-After")] 166 | RetryAfter = 25, 167 | 168 | /// 169 | /// Server 标头,指定关于起始服务器代理的信息。 170 | /// 171 | [Description("Server")] 172 | Server = 26, 173 | 174 | /// 175 | /// Set-Cookie 标头,指定提供给客户端的 Cookie 数据。 176 | /// 177 | [Description("Set-Cookie")] 178 | SetCookie = 27, 179 | 180 | /// 181 | /// Vary 标头,指定用于确定缓存的响应是否为新响应的请求标头。 182 | /// 183 | [Description("Vary")] 184 | Vary = 28, 185 | 186 | /// 187 | /// WWW-Authenticate 标头,指定客户端必须对服务器验证其自身。 188 | /// 189 | [Description("WWW-Authenticate")] 190 | WwwAuthenticate = 29, 191 | 192 | 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/ResponseHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.IO; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace HTTPServerLib 10 | { 11 | public static class ResponseHelper 12 | { 13 | public static HttpResponse FromFile(this HttpResponse response, string fileName) 14 | { 15 | if (!File.Exists(fileName)) 16 | { 17 | response.SetContent("

    404 - Not Found

    "); 18 | response.StatusCode = "404"; 19 | response.Content_Type = "text/html"; 20 | return response; 21 | } 22 | 23 | var content = File.ReadAllBytes(fileName); 24 | var contentType = GetMimeFromFile(fileName); 25 | response.SetContent(content); 26 | response.Content_Type = contentType; 27 | response.StatusCode = "200"; 28 | return response; 29 | } 30 | 31 | public static HttpResponse FromXML(this HttpResponse response, string xmlText) 32 | { 33 | response.SetContent(xmlText); 34 | response.Content_Type = "text/xml"; 35 | response.StatusCode = "200"; 36 | return response; 37 | } 38 | 39 | public static HttpResponse FromXML(this HttpResponse response, T entity) where T : class 40 | { 41 | return response.FromXML(""); 42 | } 43 | 44 | public static HttpResponse FromJSON(this HttpResponse response, string jsonText) 45 | { 46 | response.SetContent(jsonText); 47 | response.Content_Type = "text/json"; 48 | response.StatusCode = "200"; 49 | return response; 50 | } 51 | 52 | public static HttpResponse FromJSON(this HttpResponse response, T entity) where T : class 53 | { 54 | return response.FromJSON(""); 55 | } 56 | 57 | public static HttpResponse FromText(this HttpResponse response, string text) 58 | { 59 | response.SetContent(text); 60 | response.Content_Type = "text/plain"; 61 | response.StatusCode = "200"; 62 | return response; 63 | } 64 | 65 | private static string GetMimeFromFile(string filePath) 66 | { 67 | IntPtr mimeout; 68 | if (!File.Exists(filePath)) 69 | throw new FileNotFoundException(string.Format("File {0} can't be found at server.", filePath)); 70 | 71 | int MaxContent = (int)new FileInfo(filePath).Length; 72 | if (MaxContent > 4096) MaxContent = 4096; 73 | byte[] buf = new byte[MaxContent]; 74 | 75 | using (FileStream fs = File.OpenRead(filePath)) 76 | { 77 | fs.Read(buf, 0, MaxContent); 78 | fs.Close(); 79 | } 80 | 81 | int result = FindMimeFromData(IntPtr.Zero, filePath, buf, MaxContent, null, 0, out mimeout, 0); 82 | if (result != 0) 83 | throw Marshal.GetExceptionForHR(result); 84 | 85 | string mime = Marshal.PtrToStringUni(mimeout); 86 | Marshal.FreeCoTaskMem(mimeout); 87 | 88 | return mime; 89 | } 90 | 91 | [DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)] 92 | static extern int FindMimeFromData(IntPtr pBC, 93 | [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl, 94 | [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)] 95 | byte[] pBuffer, 96 | int cbSize, 97 | [MarshalAs(UnmanagedType.LPWStr)] 98 | string pwzMimeProposed, 99 | int dwMimeFlags, 100 | out IntPtr ppwzMimeOut, 101 | int dwReserved); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/RouteAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | [AttributeUsage(AttributeTargets.Method)] 10 | class RouteAttribute:Attribute 11 | { 12 | public RouteMethod Method { get; set; } 13 | public string RoutePath { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/RouteMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public enum RouteMethod 10 | { 11 | Get, 12 | Post 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/ServiceModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | 8 | namespace HTTPServerLib 9 | { 10 | public class ServiceModule 11 | { 12 | public bool SearchRoute(ServiceRoute route) 13 | { 14 | return true; 15 | } 16 | 17 | public ActionResult ExecuteRoute(ServiceRoute route) 18 | { 19 | var type = this.GetType(); 20 | var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); 21 | methods = methods.Where(m => m.ReturnType == typeof(ActionResult)).ToArray(); 22 | if (methods == null || methods.Length <= 0) return null; 23 | var method = methods.FirstOrDefault(m => 24 | { 25 | var attribute = m.GetCustomAttribute(true); 26 | if (attribute == null) return false; 27 | if (attribute.Method == route.Method && attribute.RoutePath == route.RoutePath) 28 | return true; 29 | return false; 30 | }); 31 | 32 | if (method == null) return null; 33 | return (ActionResult)method.Invoke(this, new object[] { }); 34 | 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /HTTPServer/HTTPServerLib/ServiceRoute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HTTPServerLib 8 | { 9 | public class ServiceRoute 10 | { 11 | public RouteMethod Method { get; private set; } 12 | public string RoutePath { get; private set; } 13 | 14 | public static ServiceRoute Parse(HttpRequest request) 15 | { 16 | var route = new ServiceRoute(); 17 | route.Method = (RouteMethod)Enum.Parse(typeof(RouteMethod), request.Method); 18 | route.RoutePath = request.URL; 19 | return route; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /HTTPServer/Packages.dgml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /HTTPServer/Packages/LitJson.0.7.0/LitJson.0.7.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qinyuanpei/HttpServer/f5decb7b887b3afe1b9ec55b29b8a73112851bbd/HTTPServer/Packages/LitJson.0.7.0/LitJson.0.7.0.nupkg -------------------------------------------------------------------------------- /HTTPServer/Packages/LitJson.0.7.0/lib/LitJson.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qinyuanpei/HttpServer/f5decb7b887b3afe1b9ec55b29b8a73112851bbd/HTTPServer/Packages/LitJson.0.7.0/lib/LitJson.dll -------------------------------------------------------------------------------- /HTTPServer/Packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HttpServer 2 | [![Build Status](https://www.travis-ci.org/qinyuanpei/HttpServer.svg?branch=master)](https://www.travis-ci.org/qinyuanpei/HttpServer) 3 | ![Sonar-HttpServer](https://sonarcloud.io/api/project_badges/measure?project=Sonar-HttpServer&metric=alert_status) 4 | 5 | 一个使用C#编写的简易Web服务器, 目前支持: 6 | * 静态页面处理 :smile: 7 | * GET/POST请求 :smile: 8 | * 支持HTTPS协议 :smile: 9 | * 支持返回JSON :worried: 10 | * 支持路由方法 :worried: 11 | 12 | # 快速开始 13 | 14 | ## HTTP服务器示例 15 | ``` 16 | class Program 17 | { 18 | static void Main(string[] args) 19 | { 20 | ExampleServer server = new ExampleServer("0.0.0.0",4050); 21 | server.Start(); 22 | } 23 | } 24 | ``` 25 | 26 | ## GET/POST请求示例 27 | ``` 28 | public override void OnPost(HttpRequest request, HttpResponse response) 29 | { 30 | //获取客户端传递的参数 31 | string data = request.Params == null ? "" : string.Join(";", request.Params.Select(x => x.Key + "=" + x.Value).ToArray()); 32 | 33 | //设置返回信息 34 | string content = string.Format("这是通过Post方式返回的数据:{0}", data); 35 | 36 | //构造响应报文 37 | response.SetContent(content); 38 | response.Content_Encoding = "utf-8"; 39 | response.StatusCode = "200"; 40 | response.Content_Type = "text/html; charset=UTF-8"; 41 | response.Server = "ExampleServer"; 42 | 43 | //发送响应 44 | response.Send(); 45 | } 46 | 47 | public override void OnGet(HttpRequest request, HttpResponse response) 48 | { 49 | 50 | ///链接形式1:"http://localhost:4050/assets/styles/style.css"表示访问指定文件资源, 51 | ///此时读取服务器目录下的/assets/styles/style.css文件。 52 | 53 | ///链接形式1:"http://localhost:4050/assets/styles/"表示访问指定页面资源, 54 | ///此时读取服务器目录下的/assets/styles/style.index文件。 55 | 56 | //当文件不存在时应返回404状态码 57 | string requestURL = request.URL; 58 | requestURL = requestURL.Replace("/", @"\").Replace("\\..", "").TrimStart('\\'); 59 | string requestFile = Path.Combine(ServerRoot, requestURL); 60 | 61 | //判断地址中是否存在扩展名 62 | string extension = Path.GetExtension(requestFile); 63 | 64 | //根据有无扩展名按照两种不同链接进行处 65 | if (extension != "") 66 | { 67 | //从文件中返回HTTP响应 68 | response = LoadFromFile(response, requestFile); 69 | } 70 | else 71 | { 72 | //目录存在且不存在index页面时时列举目录 73 | if (Directory.Exists(requestFile) && !File.Exists(requestFile + "\\index.html")) 74 | { 75 | requestFile = Path.Combine(ServerRoot, requestFile); 76 | var content = ListDirectory(requestFile, requestURL); 77 | response = response.SetContent(content, Encoding.UTF8); 78 | response.Content_Type = "text/html; charset=UTF-8"; 79 | } 80 | else 81 | { 82 | //加载静态HTML页面 83 | requestFile = Path.Combine(requestFile, "index.html"); 84 | response = LoadFromFile(response, requestFile); 85 | response.Content_Type = "text/html; charset=UTF-8"; 86 | } 87 | } 88 | 89 | //发送HTTP响应 90 | response.Send(); 91 | } 92 | ``` 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # must be unique in a given SonarQube instance 2 | sonar.projectKey=Sonar-HttpServer 3 | # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. 4 | sonar.projectName=HttpServer 5 | sonar.projectVersion=1.0 6 | 7 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 8 | # This property is optional if sonar.modules is set. 9 | sonar.sources=. 10 | 11 | # Encoding of the source code. Default is default system encoding 12 | #sonar.sourceEncoding=UTF-8 --------------------------------------------------------------------------------