├── Images
└── Logo_128x128.png
├── HTTPnet.Core
├── WebSockets
│ ├── WebSocketMessage.cs
│ ├── Protocol
│ │ ├── WebSocketOpcode.cs
│ │ ├── WebSocketFrame.cs
│ │ ├── WebSocketFrameWriter.cs
│ │ └── WebSocketFrameReader.cs
│ ├── WebSocketTextMessage.cs
│ ├── WebSocketBinaryMessage.cs
│ ├── WebSocketMessageReceivedEventArgs.cs
│ └── WebSocketSession.cs
├── Exceptions
│ └── HttpRequestInvalidException.cs
├── Communication
│ ├── ISessionHandler.cs
│ ├── IServerSocketWrapper.cs
│ ├── IClientSocketWrapper.cs
│ └── ClientSession.cs
├── Diagnostics
│ ├── HttpNetTraceLevel.cs
│ ├── HttpNetTraceMessagePublishedEventArgs.cs
│ └── HTTPnetTrace.cs
├── Http
│ ├── HttpVersion.cs
│ ├── IHttpRequestHandler.cs
│ ├── Raw
│ │ ├── RawHttpRequest.cs
│ │ ├── RawHttpResponse.cs
│ │ ├── RawHttpResponseWriter.cs
│ │ └── RawHttpRequestReader.cs
│ ├── HttpServerOptions.cs
│ ├── HttpMethod.cs
│ ├── HttpHeader.cs
│ ├── HttpContext.cs
│ ├── HttpHeaderExtensions.cs
│ └── HttpSessionHandler.cs
├── Pipeline
│ ├── IHttpContextPipelineExceptionHandler.cs
│ ├── IHttpContextPipelineHandler.cs
│ ├── HttpContextPipelineHandlerContext.cs
│ ├── Handlers
│ │ ├── ResponseBodyLengthHandler.cs
│ │ ├── TraceHandler.cs
│ │ ├── ResponseCompressionHandler.cs
│ │ ├── RequestBodyHandler.cs
│ │ ├── WebSocketRequestHandler.cs
│ │ └── MimeTypeProvider.cs
│ └── HttpContextPipeline.cs
├── IHttpServer.cs
├── HTTPnet.Core.csproj
└── HttpServer.cs
├── .bettercodehub.yml
├── Tests
├── HTTPnet.TestApp.NetFramework
│ ├── App.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── HTTPnet.TestApp.NetFramework.csproj
│ └── Program.cs
├── HTTPnet.Core.Tests
│ ├── packages.config
│ ├── RawHttpResponseWriterTests.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── WebSocketFrameTests.cs
│ ├── RawHttpRequestReaderTests.cs
│ └── HTTPnet.Core.Tests.csproj
└── Index.html
├── Frameworks
├── HTTPnet.NetStandard
│ ├── HttpServerFactory.cs
│ ├── HTTPnet.NetStandard.csproj
│ └── Implementations
│ │ ├── ClientSocketWrapper.cs
│ │ └── ServerSocketWrapper.cs
├── HTTPnet.UniversalWindows
│ ├── HttpServerFactory.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ └── HTTPnet.UniversalWindows.rd.xml
│ ├── Implementations
│ │ ├── ClientSocketWrapper.cs
│ │ └── ServerSocketWrapper.cs
│ └── HTTPnet.UniversalWindows.csproj
└── HTTPnet.NetFramework
│ ├── Properties
│ └── AssemblyInfo.cs
│ └── HTTPnet.NetFramework.csproj
├── Build
├── build.ps1
└── HTTPnet.nuspec
├── LICENSE
├── README.md
├── .gitignore
└── HTTPnet.sln
/Images/Logo_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chkr1011/HTTPnet/HEAD/Images/Logo_128x128.png
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/WebSocketMessage.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.WebSockets
2 | {
3 | public abstract class WebSocketMessage
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.bettercodehub.yml:
--------------------------------------------------------------------------------
1 | exclude:
2 | - /Frameworks/.*
3 | - /HTTPnet.TestApp.NetCore/Program.cs
4 | - /Tests/HTTPnet.TestApp.NetFramework/.*
5 | component_depth: 1
6 | languages:
7 | - csharp
8 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Exceptions/HttpRequestInvalidException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HTTPnet.Core.Exceptions
4 | {
5 | public class HttpRequestInvalidException : Exception
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Communication/ISessionHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace HTTPnet.Core.Communication
4 | {
5 | public interface ISessionHandler
6 | {
7 | Task ProcessAsync();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.TestApp.NetFramework/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Diagnostics/HttpNetTraceLevel.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.Diagnostics
2 | {
3 | public enum HttpNetTraceLevel
4 | {
5 | Verbose,
6 | Information,
7 | Warning,
8 | Error
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpVersion.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.Http
2 | {
3 | public class HttpVersion
4 | {
5 | public const string Version1_0 = "HTTP/1.0";
6 |
7 | public const string Version1_1 = "HTTP/1.1";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/IHttpRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace HTTPnet.Core.Http
4 | {
5 | public interface IHttpRequestHandler
6 | {
7 | Task HandleHttpRequestAsync(HttpContext httpContext);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.NetStandard/HttpServerFactory.cs:
--------------------------------------------------------------------------------
1 | using HTTPnet.Core;
2 | using HTTPnet.Implementations;
3 |
4 | namespace HTTPnet
5 | {
6 | public class HttpServerFactory
7 | {
8 | public HttpServer CreateHttpServer()
9 | {
10 | return new HttpServer(o => new ServerSocketWrapper(o));
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.UniversalWindows/HttpServerFactory.cs:
--------------------------------------------------------------------------------
1 | using HTTPnet.Core;
2 | using HTTPnet.Implementations;
3 |
4 | namespace HTTPnet
5 | {
6 | public class HttpServerFactory
7 | {
8 | public HttpServer CreateHttpServer()
9 | {
10 | return new HttpServer(o => new ServerSocketWrapper(o));
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Communication/IServerSocketWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace HTTPnet.Core.Communication
5 | {
6 | public interface IServerSocketWrapper : IDisposable
7 | {
8 | Task StartAsync();
9 | Task StopAsync();
10 |
11 | Task AcceptAsync();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/IHttpContextPipelineExceptionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using HTTPnet.Core.Http;
4 |
5 | namespace HTTPnet.Core.Pipeline
6 | {
7 | public interface IHttpContextPipelineExceptionHandler
8 | {
9 | Task HandleExceptionAsync(HttpContext httpContext, Exception exception);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/Protocol/WebSocketOpcode.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.WebSockets.Protocol
2 | {
3 | public enum WebSocketOpcode
4 | {
5 | Continuation = 0,
6 | Text = 1,
7 | Binary = 2,
8 | // 0x3-0x7 have no meaning,
9 | ConnectionClose = 8,
10 | Ping = 9,
11 | Pong = 0xA
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/HTTPnet.Core/IHttpServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using HTTPnet.Core.Http;
4 |
5 | namespace HTTPnet.Core
6 | {
7 | public interface IHttpServer : IDisposable
8 | {
9 | IHttpRequestHandler RequestHandler { get; set; }
10 |
11 | Task StartAsync(HttpServerOptions options);
12 | Task StopAsync();
13 | }
14 | }
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/IHttpContextPipelineHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace HTTPnet.Core.Pipeline
4 | {
5 | public interface IHttpContextPipelineHandler
6 | {
7 | Task ProcessRequestAsync(HttpContextPipelineHandlerContext context);
8 |
9 | Task ProcessResponseAsync(HttpContextPipelineHandlerContext context);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.NetStandard/HTTPnet.NetStandard.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.3
5 | HTTPnet
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/HTTPnet.Core/HTTPnet.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.1
5 | HTTPnet.Core
6 | HTTPnet.Core
7 | 2.1.0.0
8 | 2.1.0.0
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/Protocol/WebSocketFrame.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.WebSockets.Protocol
2 | {
3 | public class WebSocketFrame
4 | {
5 | public bool Fin { get; set; } = true;
6 | public WebSocketOpcode Opcode { get; set; } = WebSocketOpcode.Binary;
7 | public uint MaskingKey { get; set; }
8 | public byte[] Payload { get; set; } = new byte[0];
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/WebSocketTextMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HTTPnet.Core.WebSockets
4 | {
5 | public class WebSocketTextMessage : WebSocketMessage
6 | {
7 | public WebSocketTextMessage(string text)
8 | {
9 | Text = text ?? throw new ArgumentNullException(nameof(text));
10 | }
11 |
12 | public string Text { get; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/WebSocketBinaryMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HTTPnet.Core.WebSockets
4 | {
5 | public class WebSocketBinaryMessage : WebSocketMessage
6 | {
7 | public WebSocketBinaryMessage(byte[] data)
8 | {
9 | Data = data ?? throw new ArgumentNullException(nameof(data));
10 | }
11 |
12 | public byte[] Data { get; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Communication/IClientSocketWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 |
5 | namespace HTTPnet.Core.Communication
6 | {
7 | public interface IClientSocketWrapper : IDisposable
8 | {
9 | string Identifier { get; }
10 |
11 | Stream ReceiveStream { get; }
12 | Stream SendStream { get; }
13 |
14 | Task DisconnectAsync();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/Raw/RawHttpRequest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | namespace HTTPnet.Core.Http.Raw
5 | {
6 | public class RawHttpRequest
7 | {
8 | public string Method { get; set; }
9 | public string Uri { get; set; }
10 | public string Version { get; set; }
11 | public Dictionary Headers { get; set; }
12 | public Stream Body { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/Tests/HTTPnet.Core.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/Raw/RawHttpResponse.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | namespace HTTPnet.Core.Http.Raw
5 | {
6 | public class RawHttpResponse
7 | {
8 | public string Version { get; set; }
9 | public int StatusCode { get; set; }
10 | public string ReasonPhrase { get; set; }
11 | public Dictionary Headers { get; set; }
12 | public Stream Body { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpServerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.Http
2 | {
3 | public class HttpServerOptions
4 | {
5 | public static HttpServerOptions Default => new HttpServerOptions();
6 |
7 | public int Port { get; set; } = 80;
8 |
9 | public bool NoDelay { get; set; } = true;
10 |
11 | public int Backlog { get; set; } = 10;
12 |
13 | public int SendBufferSize { get; set; } = 81920;
14 |
15 | public int ReceiveChunkSize { get; set; } = 8 * 1024; // 8 KB
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpMethod.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.Http
2 | {
3 | public static class HttpMethod
4 | {
5 | public const string Get = "GET";
6 |
7 | public const string Post = "POST";
8 |
9 | public const string Put = "PUT";
10 |
11 | public const string Patch = "PATCH";
12 |
13 | public const string Delete = "DELETE";
14 |
15 | public const string Trace = "TRACE";
16 |
17 | public const string Head = "HEAD";
18 |
19 | public const string Options = "OPTIONS";
20 |
21 | public const string Connect = "CONNECT";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Build/build.ps1:
--------------------------------------------------------------------------------
1 | param([string]$version)
2 |
3 | if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
4 |
5 | $msbuild = "MSBuild.exe"
6 | &$msbuild ..\Frameworks\HTTPnet.NetFramework\HTTPnet.NetFramework.csproj /t:Build /p:Configuration="Release"
7 | &$msbuild ..\Frameworks\HTTPnet.Netstandard\HTTPnet.Netstandard.csproj /t:Build /p:Configuration="Release"
8 | &$msbuild ..\Frameworks\HTTPnet.UniversalWindows\HTTPnet.UniversalWindows.csproj /t:Build /p:Configuration="Release"
9 |
10 | Remove-Item .\NuGet -Force -Recurse
11 | New-Item -ItemType Directory -Force -Path .\NuGet
12 | .\NuGet.exe pack HTTPnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $version
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/HttpContextPipelineHandlerContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using HTTPnet.Core.Http;
4 |
5 | namespace HTTPnet.Core.Pipeline
6 | {
7 | public class HttpContextPipelineHandlerContext
8 | {
9 | public HttpContextPipelineHandlerContext(HttpContext httpContext)
10 | {
11 | HttpContext = httpContext ?? throw new ArgumentNullException(nameof(httpContext));
12 | }
13 |
14 | public Dictionary Properties { get; } = new Dictionary();
15 | public HttpContext HttpContext { get; }
16 | public bool BreakPipeline { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/WebSocketMessageReceivedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HTTPnet.Core.WebSockets
4 | {
5 | public class WebSocketMessageReceivedEventArgs : EventArgs
6 | {
7 | public WebSocketMessageReceivedEventArgs(WebSocketMessage message, WebSocketSession webSocketSession)
8 | {
9 | Message = message ?? throw new ArgumentNullException(nameof(message));
10 | WebSocketSession = webSocketSession ?? throw new ArgumentNullException(nameof(webSocketSession));
11 | }
12 |
13 | public WebSocketSession WebSocketSession { get; }
14 |
15 | public WebSocketMessage Message { get; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Diagnostics/HttpNetTraceMessagePublishedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HTTPnet.Core.Diagnostics
4 | {
5 | public sealed class HttpNetTraceMessagePublishedEventArgs : EventArgs
6 | {
7 | public HttpNetTraceMessagePublishedEventArgs(int threadId, string source, HttpNetTraceLevel level, string message, Exception exception)
8 | {
9 | ThreadId = threadId;
10 | Source = source;
11 | Level = level;
12 | Message = message;
13 | Exception = exception;
14 | }
15 |
16 | public int ThreadId { get; }
17 |
18 | public string Source { get; }
19 |
20 | public HttpNetTraceLevel Level { get; }
21 |
22 | public string Message { get; }
23 |
24 | public Exception Exception { get; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/Handlers/ResponseBodyLengthHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Threading.Tasks;
3 | using HTTPnet.Core.Http;
4 |
5 | namespace HTTPnet.Core.Pipeline.Handlers
6 | {
7 | public class ResponseBodyLengthHandler : IHttpContextPipelineHandler
8 | {
9 | public Task ProcessRequestAsync(HttpContextPipelineHandlerContext context)
10 | {
11 | return Task.FromResult(0);
12 | }
13 |
14 | public Task ProcessResponseAsync(HttpContextPipelineHandlerContext context)
15 | {
16 | var bodyLength = context.HttpContext.Response.Body?.Length ?? 0;
17 | context.HttpContext.Response.Headers[HttpHeader.ContentLength] = bodyLength.ToString(CultureInfo.InvariantCulture);
18 |
19 | return Task.FromResult(0);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpHeader.cs:
--------------------------------------------------------------------------------
1 | namespace HTTPnet.Core.Http
2 | {
3 | public static class HttpHeader
4 | {
5 | public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
6 |
7 | public const string AcceptEncoding = "Accept-Encoding";
8 |
9 | public const string Connection = "Connection";
10 |
11 | public const string ContentType = "Content-Type";
12 |
13 | public const string ContentEncoding = "Content-Encoding";
14 |
15 | public const string ContentLength = "Content-Length";
16 |
17 | public const string IfNoneMatch = "If-None-Match";
18 |
19 | public const string ETag = "ETag";
20 |
21 | public const string Expect = "Expect";
22 |
23 | public const string Upgrade = "Upgrade";
24 |
25 | public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
26 |
27 | public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
28 |
29 | public const string SecWebSocketKey = "Sec-WebSocket-Key";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Christian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using HTTPnet.Core.Communication;
4 | using HTTPnet.Core.Http.Raw;
5 |
6 | namespace HTTPnet.Core.Http
7 | {
8 | public class HttpContext
9 | {
10 | public HttpContext(RawHttpRequest request, RawHttpResponse response, ClientSession clientSession, HttpSessionHandler sessionHandler)
11 | {
12 | ClientSession = clientSession ?? throw new ArgumentNullException(nameof(clientSession));
13 | SessionHandler = sessionHandler ?? throw new ArgumentNullException(nameof(sessionHandler));
14 | Request = request ?? throw new ArgumentNullException(nameof(request));
15 | Response = response ?? throw new ArgumentNullException(nameof(response));
16 | }
17 |
18 | public RawHttpRequest Request { get; }
19 | public RawHttpResponse Response { get; }
20 |
21 | public Dictionary Properties { get; } = new Dictionary();
22 | public HttpSessionHandler SessionHandler { get; }
23 | public ClientSession ClientSession { get; }
24 | public bool CloseConnection { get; set; }
25 | }
26 | }
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.UniversalWindows/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("HTTPnet.UniversalWindows")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("HTTPnet.UniversalWindows")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Version information for an assembly consists of the following four values:
18 | //
19 | // Major Version
20 | // Minor Version
21 | // Build Number
22 | // Revision
23 | //
24 | // You can specify all the values or you can default the Build and Revision Numbers
25 | // by using the '*' as shown below:
26 | // [assembly: AssemblyVersion("1.0.*")]
27 | [assembly: AssemblyVersion("1.0.0.0")]
28 | [assembly: AssemblyFileVersion("1.0.0.0")]
29 | [assembly: ComVisible(false)]
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.NetStandard/Implementations/ClientSocketWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net.Sockets;
4 | using System.Threading.Tasks;
5 | using HTTPnet.Core.Communication;
6 |
7 | namespace HTTPnet.Implementations
8 | {
9 | public class ClientSocketWrapper : IClientSocketWrapper
10 | {
11 | private readonly Socket _socket;
12 |
13 | public ClientSocketWrapper(Socket socket)
14 | {
15 | _socket = socket ?? throw new ArgumentNullException(nameof(socket));
16 |
17 | Identifier = socket.RemoteEndPoint.ToString();
18 |
19 | ReceiveStream = new NetworkStream(socket, true);
20 | SendStream = ReceiveStream;
21 | }
22 |
23 | public string Identifier { get; }
24 |
25 | public Stream ReceiveStream { get; }
26 | public Stream SendStream { get; }
27 |
28 | public Task DisconnectAsync()
29 | {
30 | _socket.Shutdown(SocketShutdown.Both);
31 | return Task.FromResult(0);
32 | }
33 |
34 | public void Dispose()
35 | {
36 | ReceiveStream?.Dispose();
37 | SendStream?.Dispose();
38 |
39 | _socket?.Dispose();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.UniversalWindows/Implementations/ClientSocketWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net.Sockets;
4 | using System.Threading.Tasks;
5 | using HTTPnet.Core.Communication;
6 |
7 | namespace HTTPnet.Implementations
8 | {
9 | public class ClientSocketWrapper : IClientSocketWrapper
10 | {
11 | private readonly Socket _socket;
12 |
13 | public ClientSocketWrapper(Socket socket)
14 | {
15 | _socket = socket ?? throw new ArgumentNullException(nameof(socket));
16 |
17 | Identifier = socket.RemoteEndPoint.ToString();
18 |
19 | ReceiveStream = new NetworkStream(socket, true);
20 | SendStream = ReceiveStream;
21 | }
22 |
23 | public string Identifier { get; }
24 |
25 | public Stream ReceiveStream { get; }
26 | public Stream SendStream { get; }
27 |
28 | public Task DisconnectAsync()
29 | {
30 | _socket.Shutdown(SocketShutdown.Both);
31 | return Task.FromResult(0);
32 | }
33 |
34 | public void Dispose()
35 | {
36 | ReceiveStream?.Dispose();
37 | SendStream?.Dispose();
38 |
39 | _socket?.Dispose();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpHeaderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace HTTPnet.Core.Http
5 | {
6 | public static class HttpHeaderExtensions
7 | {
8 | public static bool ConnectionMustBeClosed(this Dictionary headers)
9 | {
10 | return headers.ValueEquals(HttpHeader.Connection, "Close");
11 | }
12 |
13 | public static bool RequiresContinue(this Dictionary headers)
14 | {
15 | return headers.TryGetValue(HttpHeader.Expect, out var value) && string.Equals(value, "100-Continue", StringComparison.OrdinalIgnoreCase);
16 | }
17 |
18 | public static bool ValueEquals(this Dictionary headers, string headerName, string expectedValue)
19 | {
20 | if (headers == null) throw new ArgumentNullException(nameof(headers));
21 | if (headerName == null) throw new ArgumentNullException(nameof(headerName));
22 | if (expectedValue == null) throw new ArgumentNullException(nameof(expectedValue));
23 |
24 | if (!headers.TryGetValue(headerName, out var value))
25 | {
26 | return false;
27 | }
28 |
29 | return string.Equals(value, expectedValue, StringComparison.OrdinalIgnoreCase);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/Handlers/TraceHandler.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 | using HTTPnet.Core.Diagnostics;
5 |
6 | namespace HTTPnet.Core.Pipeline.Handlers
7 | {
8 | public class TraceHandler : IHttpContextPipelineHandler
9 | {
10 | public Task ProcessRequestAsync(HttpContextPipelineHandlerContext context)
11 | {
12 | var body = "";
13 |
14 | if (context.HttpContext.Request.Body != null)
15 | {
16 | using (var streamReader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8, false, 1024, true))
17 | {
18 | body = streamReader.ReadToEnd();
19 | }
20 |
21 | context.HttpContext.Request.Body.Position = 0;
22 | }
23 |
24 | HttpNetTrace.Verbose(nameof(TraceHandler), context.HttpContext.Request.Method + " " + context.HttpContext.Request.Uri + " " + body);
25 | return Task.FromResult(0);
26 | }
27 |
28 | public Task ProcessResponseAsync(HttpContextPipelineHandlerContext context)
29 | {
30 | HttpNetTrace.Verbose(nameof(TraceHandler), context.HttpContext.Response.StatusCode + " " + context.HttpContext.Response.ReasonPhrase);
31 | return Task.FromResult(0);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.Core.Tests/RawHttpResponseWriterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using HTTPnet.Core.Http;
7 | using HTTPnet.Core.Http.Raw;
8 | using Microsoft.VisualStudio.TestTools.UnitTesting;
9 | using System.Net;
10 |
11 | namespace HTTPnet.Core.Tests
12 | {
13 | [TestClass]
14 | public class RawHttpResponseWriterTests
15 | {
16 | [TestMethod]
17 | public void Http_SerializeHttpRequest()
18 | {
19 | var response = new RawHttpResponse
20 | {
21 | StatusCode = (int) HttpStatusCode.BadRequest,
22 | Body = new MemoryStream(Encoding.UTF8.GetBytes("{\"text\":1234}")),
23 | Headers =
24 | {
25 | ["A"] = 1.ToString(),
26 | ["B"] = "x"
27 | }
28 | };
29 |
30 | var memoryStream = new MemoryStream();
31 | var serializer = new RawHttpResponseWriter(memoryStream, HttpServerOptions.Default);
32 | serializer.WriteAsync(response, CancellationToken.None).Wait();
33 |
34 | var requiredBuffer = Convert.FromBase64String("SFRUUC8xLjEgNDAwIEJhZFJlcXVlc3QNCkE6MQ0KQjp4DQpDb250ZW50LVR5cGU6dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA0KQ29udGVudC1MZW5ndGg6MTMNCg0KeyJ0ZXh0IjoxMjM0fQ==");
35 | Assert.IsTrue(memoryStream.ToArray().SequenceEqual(requiredBuffer));
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.Core.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("HTTPnet.Core.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("HTTPnet.Core.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("59ffe02a-60a3-4762-b2e5-3b4e44939101")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.NetFramework/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("HTTPnet.NetFramework")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("HTTPnet.NetFramework")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("fd9daefc-98de-43c6-ae07-ec60d2bcb78b")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.TestApp.NetFramework/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("HTTPnet.TestApp.NetFramework")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("HTTPnet.TestApp.NetFramework")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d65ca58a-3b9e-41bb-826e-39510354f7a5")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Tests/Index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
39 |
40 | Test
41 |
42 |
43 | WebSockets
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.UniversalWindows/Properties/HTTPnet.UniversalWindows.rd.xml:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://www.nuget.org/packages/HTTPnet)
6 | [](https://bettercodehub.com/)
7 |
8 | # HTTPnet
9 | HTTPnet is a .NET library for HTTP and WebSocket based communication. It provides a HTTP/WebSocket server and a powerful processing pipeline for HTTP request and their responses.
10 |
11 | # Features
12 |
13 | ### General
14 | * Async support
15 | * HTTP context pipeline for powerful request and response processing
16 | * Support for WebSocket connections
17 | * Lightweight (only the low level implementation of HTTP, no overhead)
18 | * Access to internal trace messages
19 |
20 | # Supported HTTP features
21 | * Compressed responses with Gzip
22 | * Expect header (100-Continue) for large bodies
23 | * Keep-Alive connections
24 | * WebSockets
25 |
26 | # Supported frameworks
27 | * .NET Standard 1.3+
28 | * .NET Core 1.1+
29 | * .NET Core App 1.1+
30 | * .NET Framework 4.5.2+ (x86, x64, AnyCPU)
31 | * Universal Windows (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU)
32 | * Mono 5.2+
33 |
34 | # Supported HTTP versions
35 | * 1.1
36 | * 1.0
37 |
38 | # Nuget
39 | This library is available as a nuget package: https://www.nuget.org/packages/HTTPnet/
40 |
41 | # Examples
42 | Please find examples and the documentation at the Wiki of this repository (https://github.com/chkr1011/HTTPnet/wiki).
43 |
44 | # Contributions
45 | If you want to contribute to this project just create a pull request.
46 |
47 | # References
48 | This library is used in the following projects:
49 |
50 | * HA4IoT (Open Source Home Automation system for .NET, https://github.com/chkr1011/HA4IoT)
51 |
52 | If you use this library and want to see your project here please let me know.
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/Handlers/ResponseCompressionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.IO.Compression;
5 | using System.Threading.Tasks;
6 | using HTTPnet.Core.Http;
7 |
8 | namespace HTTPnet.Core.Pipeline.Handlers
9 | {
10 | public class ResponseCompressionHandler : IHttpContextPipelineHandler
11 | {
12 | public Task ProcessRequestAsync(HttpContextPipelineHandlerContext context)
13 | {
14 | return Task.FromResult(0);
15 | }
16 |
17 | public async Task ProcessResponseAsync(HttpContextPipelineHandlerContext context)
18 | {
19 | if (context.HttpContext.Response.Body == null || context.HttpContext.Response.Body.Length == 0)
20 | {
21 | return;
22 | }
23 |
24 | if (!ClientSupportsGzipCompression(context.HttpContext.Request.Headers))
25 | {
26 | return;
27 | }
28 |
29 | context.HttpContext.Response.Headers[HttpHeader.ContentEncoding] = "gzip";
30 |
31 | var compressedBody = new MemoryStream();
32 | using (var zipStream = new GZipStream(compressedBody, CompressionLevel.Fastest, true))
33 | {
34 | context.HttpContext.Response.Body.Position = 0;
35 | await context.HttpContext.Response.Body.CopyToAsync(zipStream);
36 | }
37 |
38 | compressedBody.Position = 0;
39 | context.HttpContext.Response.Body = compressedBody;
40 | }
41 |
42 | private static bool ClientSupportsGzipCompression(Dictionary headers)
43 | {
44 | if (headers.TryGetValue(HttpHeader.AcceptEncoding, out var headerValue))
45 | {
46 | return headerValue.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) > -1;
47 | }
48 |
49 | return false;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/Raw/RawHttpResponseWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace HTTPnet.Core.Http.Raw
8 | {
9 | public class RawHttpResponseWriter
10 | {
11 | private readonly Stream _sendStream;
12 | private readonly HttpServerOptions _options;
13 |
14 | public RawHttpResponseWriter(Stream sendStream, HttpServerOptions options)
15 | {
16 | _sendStream = sendStream ?? throw new ArgumentNullException(nameof(sendStream));
17 | _options = options ?? throw new ArgumentNullException(nameof(options));
18 | }
19 |
20 | public async Task WriteAsync(RawHttpResponse response, CancellationToken cancellationToken)
21 | {
22 | if (response == null) throw new ArgumentNullException(nameof(response));
23 | if (cancellationToken == null) throw new ArgumentNullException(nameof(cancellationToken));
24 |
25 | var buffer = new StringBuilder();
26 | buffer.AppendLine(response.Version + " " + response.StatusCode + " " + response.ReasonPhrase);
27 |
28 | foreach (var header in response.Headers)
29 | {
30 | buffer.AppendLine(header.Key + ":" + header.Value);
31 | }
32 |
33 | buffer.AppendLine();
34 |
35 | var binaryBuffer = Encoding.UTF8.GetBytes(buffer.ToString());
36 |
37 | await _sendStream.WriteAsync(binaryBuffer, 0, binaryBuffer.Length, cancellationToken).ConfigureAwait(false);
38 |
39 | if (response.Body != null && response.Body.Length > 0)
40 | {
41 | response.Body.Position = 0;
42 | await response.Body.CopyToAsync(_sendStream, _options.SendBufferSize, cancellationToken).ConfigureAwait(false);
43 | }
44 |
45 | await _sendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.NetStandard/Implementations/ServerSocketWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Threading.Tasks;
5 | using HTTPnet.Core.Communication;
6 | using HTTPnet.Core.Http;
7 |
8 | namespace HTTPnet.Implementations
9 | {
10 | public class ServerSocketWrapper : IServerSocketWrapper
11 | {
12 | private readonly HttpServerOptions _options;
13 | private Socket _listener;
14 |
15 | public ServerSocketWrapper(HttpServerOptions options)
16 | {
17 | _options = options ?? throw new ArgumentNullException(nameof(options));
18 | }
19 |
20 | public Task StartAsync()
21 | {
22 | if (_listener != null)
23 | {
24 | throw new InvalidOperationException("Already started.");
25 | }
26 |
27 | _listener = new Socket(SocketType.Stream, ProtocolType.Tcp)
28 | {
29 | NoDelay = _options.NoDelay
30 | };
31 |
32 | _listener.Bind(new IPEndPoint(IPAddress.Any, _options.Port));
33 | _listener.Listen(_options.Backlog);
34 |
35 | return Task.FromResult(0);
36 | }
37 |
38 | public Task StopAsync()
39 | {
40 | if (_listener == null)
41 | {
42 | return Task.FromResult(0);
43 | }
44 |
45 | _listener.Shutdown(SocketShutdown.Both);
46 | _listener.Dispose();
47 | _listener = null;
48 |
49 | return Task.FromResult(0);
50 | }
51 |
52 | public void Dispose()
53 | {
54 | _listener?.Shutdown(SocketShutdown.Both);
55 | _listener?.Dispose();
56 | _listener = null;
57 | }
58 |
59 | public async Task AcceptAsync()
60 | {
61 | var clientSocket = await _listener.AcceptAsync();
62 | clientSocket.NoDelay = _options.NoDelay;
63 |
64 | return new ClientSocketWrapper(clientSocket);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Communication/ClientSession.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using HTTPnet.Core.Http;
6 |
7 | namespace HTTPnet.Core.Communication
8 | {
9 | public sealed class ClientSession : IDisposable
10 | {
11 | private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
12 | private readonly HttpServer _httpServer;
13 | private ISessionHandler _sessionHandler;
14 |
15 | public ClientSession(IClientSocketWrapper client, HttpServer httpServer, HttpServerOptions options)
16 | {
17 | if (options == null) throw new ArgumentNullException(nameof(options));
18 | _httpServer = httpServer ?? throw new ArgumentNullException(nameof(httpServer));
19 | Client = client ?? throw new ArgumentNullException(nameof(client));
20 |
21 | _sessionHandler = new HttpSessionHandler(this, options);
22 | }
23 |
24 | public CancellationToken CancellationToken => _cancellationTokenSource.Token;
25 | public IClientSocketWrapper Client { get; }
26 |
27 | public async Task RunAsync()
28 | {
29 | while (!_cancellationTokenSource.IsCancellationRequested)
30 | {
31 | Debug.Assert(_sessionHandler != null);
32 |
33 | await _sessionHandler.ProcessAsync();
34 | }
35 | }
36 |
37 | public Task HandleHttpRequestAsync(HttpContext httpContext)
38 | {
39 | if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
40 |
41 | return _httpServer.HandleHttpRequestAsync(httpContext);
42 | }
43 |
44 | public void SwitchProtocol(ISessionHandler sessionHandler)
45 | {
46 | _sessionHandler = sessionHandler ?? throw new ArgumentNullException(nameof(sessionHandler));
47 | }
48 |
49 | public void Close()
50 | {
51 | _cancellationTokenSource?.Cancel(false);
52 | }
53 |
54 | public void Dispose()
55 | {
56 | Close();
57 | Client?.Dispose();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/Handlers/RequestBodyHandler.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading.Tasks;
3 | using HTTPnet.Core.Http;
4 | using HTTPnet.Core.Http.Raw;
5 | using System.Net;
6 |
7 | namespace HTTPnet.Core.Pipeline.Handlers
8 | {
9 | public class RequestBodyHandler : IHttpContextPipelineHandler
10 | {
11 | public async Task ProcessRequestAsync(HttpContextPipelineHandlerContext context)
12 | {
13 | var bodyLength = 0;
14 | if (context.HttpContext.Request.Headers.TryGetValue(HttpHeader.ContentLength, out var v))
15 | {
16 | bodyLength = int.Parse(v);
17 | }
18 |
19 | if (bodyLength == 0)
20 | {
21 | context.HttpContext.Request.Body = new MemoryStream(0);
22 | return;
23 | }
24 |
25 | if (context.HttpContext.Request.Headers.ValueEquals(HttpHeader.Expect, "100-Continue"))
26 | {
27 | var response = new RawHttpResponse
28 | {
29 | Version = context.HttpContext.Request.Version,
30 | StatusCode = (int)HttpStatusCode.Continue
31 | };
32 |
33 | await context.HttpContext.SessionHandler.ResponseWriter.WriteAsync(response, context.HttpContext.ClientSession.CancellationToken);
34 | }
35 |
36 | while (context.HttpContext.SessionHandler.RequestReader.BufferLength < bodyLength)
37 | {
38 | await context.HttpContext.SessionHandler.RequestReader.FetchChunk(context.HttpContext.ClientSession.CancellationToken);
39 | }
40 |
41 | context.HttpContext.Request.Body = new MemoryStream(bodyLength);
42 | for (var i = 0; i < bodyLength; i++)
43 | {
44 | context.HttpContext.Request.Body.WriteByte(context.HttpContext.SessionHandler.RequestReader.DequeueFromBuffer());
45 | }
46 |
47 | context.HttpContext.Request.Body.Position = 0;
48 | }
49 |
50 | public Task ProcessResponseAsync(HttpContextPipelineHandlerContext context)
51 | {
52 | return Task.FromResult(0);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Build/HTTPnet.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HTTPnet
5 | 2.1.0
6 | Christian Kratky
7 | Christian Kratky
8 | https://github.com/chkr1011/HTTPnet/blob/master/LICENSE
9 | https://github.com/chkr1011/HTTPnet
10 | https://raw.githubusercontent.com/chkr1011/HTTPnet/master/Images/Logo_128x128.png
11 | false
12 | HTTPnet is a .NET library for HTTP and WebSocket based communication. It provides a HTTP/WebSocket server and a powerful processing pipeline for HTTP request and their responses.
13 | * Initial version
14 |
15 | Copyright Christian Kratky 2017
16 | HTTP Server NETStandard IoT InternetOfThings REST MVC Internet Ajax Web HTML JavaScript WebSocket WS
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.UniversalWindows/Implementations/ServerSocketWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Threading.Tasks;
5 | using HTTPnet.Core.Communication;
6 | using HTTPnet.Core.Http;
7 |
8 | namespace HTTPnet.Implementations
9 | {
10 | public class ServerSocketWrapper : IServerSocketWrapper
11 | {
12 | private readonly HttpServerOptions _options;
13 | private Socket _listener;
14 |
15 | public ServerSocketWrapper(HttpServerOptions options)
16 | {
17 | _options = options ?? throw new ArgumentNullException(nameof(options));
18 | }
19 |
20 | public Task StartAsync()
21 | {
22 | if (_listener != null)
23 | {
24 | throw new InvalidOperationException("Already started.");
25 | }
26 |
27 | _listener = new Socket(SocketType.Stream, ProtocolType.Tcp)
28 | {
29 | NoDelay = _options.NoDelay
30 | };
31 |
32 | _listener.Bind(new IPEndPoint(IPAddress.Any, _options.Port));
33 | _listener.Listen(_options.Backlog);
34 |
35 | return Task.FromResult(0);
36 | }
37 |
38 | public Task StopAsync()
39 | {
40 | if (_listener == null)
41 | {
42 | return Task.FromResult(0);
43 | }
44 |
45 | _listener.Shutdown(SocketShutdown.Both);
46 | _listener.Dispose();
47 | _listener = null;
48 |
49 | return Task.FromResult(0);
50 | }
51 |
52 | public void Dispose()
53 | {
54 | _listener?.Shutdown(SocketShutdown.Both);
55 | _listener?.Dispose();
56 | _listener = null;
57 | }
58 |
59 | public async Task AcceptAsync()
60 | {
61 | #if (WINDOWS_UWP)
62 | var clientSocket = await _listener.AcceptAsync();
63 | #else
64 | var clientSocket = await Task.Factory.FromAsync(_listener.BeginAccept, _listener.EndAccept, null).ConfigureAwait(false);
65 | #endif
66 |
67 | clientSocket.NoDelay = _options.NoDelay;
68 |
69 | return new ClientSocketWrapper(clientSocket);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/Handlers/WebSocketRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 | using HTTPnet.Core.Http;
5 | using HTTPnet.Core.WebSockets;
6 | using System.Net;
7 |
8 | namespace HTTPnet.Core.Pipeline.Handlers
9 | {
10 | public class WebSocketRequestHandler : IHttpContextPipelineHandler
11 | {
12 | private readonly Action _sessionCreated;
13 | private readonly Func _sha1Computor;
14 |
15 | public WebSocketRequestHandler(Func sha1Computor, Action sessionCreated)
16 | {
17 | _sessionCreated = sessionCreated ?? throw new ArgumentNullException(nameof(sessionCreated));
18 | _sha1Computor = sha1Computor ?? throw new ArgumentNullException(nameof(sha1Computor));
19 | }
20 |
21 | public Task ProcessRequestAsync(HttpContextPipelineHandlerContext context)
22 | {
23 | var isWebSocketRequest = context.HttpContext.Request.Headers.ValueEquals(HttpHeader.Upgrade, "websocket");
24 | if (!isWebSocketRequest)
25 | {
26 | return Task.FromResult(0);
27 | }
28 |
29 | var webSocketKey = context.HttpContext.Request.Headers[HttpHeader.SecWebSocketKey];
30 | var responseKey = webSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
31 | var responseKeyBuffer = Encoding.UTF8.GetBytes(responseKey);
32 |
33 | var hash = _sha1Computor(responseKeyBuffer);
34 | var secWebSocketAccept = Convert.ToBase64String(hash);
35 |
36 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols;
37 | context.HttpContext.Response.Headers[HttpHeader.Connection] = "Upgrade";
38 | context.HttpContext.Response.Headers[HttpHeader.Upgrade] = "websocket";
39 | context.HttpContext.Response.Headers[HttpHeader.SecWebSocketAccept] = secWebSocketAccept;
40 |
41 | var webSocketSession = new WebSocketSession(context.HttpContext.ClientSession);
42 | context.HttpContext.ClientSession.SwitchProtocol(webSocketSession);
43 |
44 | _sessionCreated(webSocketSession);
45 |
46 | context.BreakPipeline = true;
47 |
48 | return Task.FromResult(0);
49 | }
50 |
51 | public Task ProcessResponseAsync(HttpContextPipelineHandlerContext context)
52 | {
53 | return Task.FromResult(0);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/Handlers/MimeTypeProvider.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace HTTPnet.Core.Pipeline.Handlers
4 | {
5 | public static class MimeTypeProvider
6 | {
7 | public const string Csv = "text/csv; charset=utf-8";
8 | public const string Html = "text/html; charset=utf-8";
9 | public const string Javascript = "text/javascript; charset=utf-8";
10 | public const string Json = "application/json; charset=utf-8";
11 | public const string Css = "text/css; charset=utf-8";
12 | public const string Png = "image/png";
13 | public const string Jpg = "image/jpg";
14 | public const string Manifest = "text/cache-manifest; charset=utf-8";
15 | public const string PlainText = "text/plain; charset=utf-8";
16 | public const string OctetStream = "application/octet-stream";
17 |
18 | public static string GetMimeTypeFromFilename(string filename)
19 | {
20 | string extension = Path.GetExtension(filename).ToLower();
21 | switch (extension)
22 | {
23 | case ".csv":
24 | {
25 | return Csv;
26 | }
27 |
28 | case ".html":
29 | case ".htm":
30 | {
31 | return Html;
32 | }
33 |
34 | case ".js":
35 | {
36 | return Javascript;
37 | }
38 |
39 | case ".json":
40 | {
41 | return Json;
42 | }
43 |
44 | case ".css":
45 | {
46 | return Css;
47 | }
48 |
49 | case ".png":
50 | {
51 | return Png;
52 | }
53 |
54 | case ".jpeg":
55 | case ".jpg":
56 | {
57 | return Jpg;
58 | }
59 |
60 | case ".bin":
61 | {
62 | return OctetStream;
63 | }
64 |
65 | case ".manifest":
66 | {
67 | return Manifest;
68 | }
69 |
70 | default:
71 | {
72 | return PlainText;
73 | }
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Diagnostics/HTTPnetTrace.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HTTPnet.Core.Diagnostics
4 | {
5 | public static class HttpNetTrace
6 | {
7 | public static event EventHandler TraceMessagePublished;
8 |
9 | public static void Verbose(string source, string message, params object[] parameters)
10 | {
11 | Publish(source, HttpNetTraceLevel.Verbose, null, message, parameters);
12 | }
13 |
14 | public static void Information(string source, string message, params object[] parameters)
15 | {
16 | Publish(source, HttpNetTraceLevel.Information, null, message, parameters);
17 | }
18 |
19 | public static void Warning(string source, string message, params object[] parameters)
20 | {
21 | Publish(source, HttpNetTraceLevel.Warning, null, message, parameters);
22 | }
23 |
24 | public static void Warning(string source, Exception exception, string message, params object[] parameters)
25 | {
26 | Publish(source, HttpNetTraceLevel.Warning, exception, message, parameters);
27 | }
28 |
29 | public static void Error(string source, string message, params object[] parameters)
30 | {
31 | Publish(source, HttpNetTraceLevel.Error, null, message, parameters);
32 | }
33 |
34 | public static void Error(string source, Exception exception, string message, params object[] parameters)
35 | {
36 | Publish(source, HttpNetTraceLevel.Error, exception, message, parameters);
37 | }
38 |
39 | private static void Publish(string source, HttpNetTraceLevel traceLevel, Exception exception, string message, params object[] parameters)
40 | {
41 | var handler = TraceMessagePublished;
42 | if (handler == null)
43 | {
44 | return;
45 | }
46 |
47 | if (parameters?.Length > 0)
48 | {
49 | try
50 | {
51 | message = string.Format(message, parameters);
52 | }
53 | catch (Exception formatException)
54 | {
55 | Error(nameof(HttpNetTrace), formatException, "Error while tracing message: " + message);
56 | return;
57 | }
58 | }
59 |
60 | handler.Invoke(null, new HttpNetTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, traceLevel, message, exception));
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/Protocol/WebSocketFrameWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace HTTPnet.Core.WebSockets.Protocol
7 | {
8 | public class WebSocketFrameWriter
9 | {
10 | private readonly Stream _sendStream;
11 |
12 | public WebSocketFrameWriter(Stream sendStream)
13 | {
14 | _sendStream = sendStream ?? throw new ArgumentNullException(nameof(sendStream));
15 | }
16 |
17 | public async Task WriteAsync(WebSocketFrame frame, CancellationToken cancellationToken)
18 | {
19 | // https://tools.ietf.org/html/rfc6455
20 |
21 | var buffer = new byte[10];
22 | var frameSize = 2;
23 |
24 | if (frame.Fin)
25 | {
26 | buffer[0] |= 128;
27 | }
28 |
29 | buffer[0] |= (byte)frame.Opcode;
30 |
31 | if (frame.MaskingKey != 0)
32 | {
33 | buffer[1] |= 128;
34 | }
35 |
36 | var payloadLength = frame.Payload?.Length ?? 0;
37 |
38 | if (payloadLength > 0)
39 | {
40 | if (payloadLength <= 125)
41 | {
42 | buffer[1] |= (byte)payloadLength;
43 | }
44 | else if (payloadLength >= 126 && payloadLength <= 65535)
45 | {
46 | buffer[1] |= 126;
47 | buffer[2] = (byte)(payloadLength >> 8);
48 | buffer[3] = (byte)payloadLength;
49 | frameSize = 4;
50 | }
51 | else
52 | {
53 | buffer[1] |= 127;
54 | buffer[2] = (byte)(payloadLength >> 56);
55 | buffer[3] = (byte)(payloadLength >> 48);
56 | buffer[4] = (byte)(payloadLength >> 40);
57 | buffer[5] = (byte)(payloadLength >> 32);
58 | buffer[6] = (byte)(payloadLength >> 24);
59 | buffer[7] = (byte)(payloadLength >> 16);
60 | buffer[8] = (byte)(payloadLength >> 8);
61 | buffer[9] = (byte)payloadLength;
62 | frameSize = 10;
63 | }
64 | }
65 |
66 | await _sendStream.WriteAsync(buffer, 0, frameSize, cancellationToken).ConfigureAwait(false);
67 | await _sendStream.WriteAsync(frame.Payload, 0, payloadLength, cancellationToken).ConfigureAwait(false);
68 | await _sendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.NetFramework/HTTPnet.NetFramework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}
8 | Library
9 | Properties
10 | HTTPnet
11 | HTTPnet.NetFramework
12 | v4.5
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 | HttpServerFactory.cs
41 |
42 |
43 | Implementations\ClientSocketWrapper.cs
44 |
45 |
46 | Implementations\ServerSocketWrapper.cs
47 |
48 |
49 |
50 |
51 |
52 | {26939955-72a0-41f3-a68c-f04def24e326}
53 | HTTPnet.Core
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.TestApp.NetFramework/HTTPnet.TestApp.NetFramework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}
8 | Exe
9 | HTTPnet.TestApp.NetFramework
10 | HTTPnet.TestApp.NetFramework
11 | v4.6.1
12 | 512
13 | true
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Designer
46 |
47 |
48 |
49 |
50 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}
51 | HTTPnet.NetFramework
52 |
53 |
54 | {26939955-72A0-41F3-A68C-F04DEF24E326}
55 | HTTPnet.Core
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/HttpSessionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using HTTPnet.Core.Communication;
5 | using HTTPnet.Core.Diagnostics;
6 | using HTTPnet.Core.Http.Raw;
7 | using System.Net;
8 |
9 | namespace HTTPnet.Core.Http
10 | {
11 | public sealed class HttpSessionHandler : ISessionHandler
12 | {
13 | private readonly ClientSession _clientSession;
14 | private readonly HttpServerOptions _options;
15 |
16 | public HttpSessionHandler(ClientSession clientSession, HttpServerOptions options)
17 | {
18 | _options = options ?? throw new ArgumentNullException(nameof(options));
19 | _clientSession = clientSession ?? throw new ArgumentNullException(nameof(clientSession));
20 |
21 | RequestReader = new RawHttpRequestReader(clientSession.Client.ReceiveStream, options);
22 | ResponseWriter = new RawHttpResponseWriter(clientSession.Client.SendStream, options);
23 | }
24 |
25 | public RawHttpRequestReader RequestReader { get; }
26 | public RawHttpResponseWriter ResponseWriter { get; }
27 |
28 | public async Task ProcessAsync()
29 | {
30 | RawHttpRequest httpRequest;
31 | try
32 | {
33 | httpRequest = await RequestReader.ReadAsync(_clientSession.CancellationToken).ConfigureAwait(false);
34 | }
35 | catch (OperationCanceledException)
36 | {
37 | _clientSession.Close();
38 | return;
39 | }
40 | catch (Exception exception)
41 | {
42 | HttpNetTrace.Error(nameof(HttpSessionHandler), exception, "Unhandled exceptio while processing HTTP request.");
43 | _clientSession.Close();
44 | return;
45 | }
46 |
47 | var httpResponse = new RawHttpResponse
48 | {
49 | Version = httpRequest.Version,
50 | Headers = new Dictionary(),
51 | StatusCode = (int)HttpStatusCode.OK
52 | };
53 |
54 | var httpContext = new HttpContext(httpRequest, httpResponse, _clientSession, this);
55 | await _clientSession.HandleHttpRequestAsync(httpContext);
56 |
57 | if (httpContext.Response != null)
58 | {
59 | await ResponseWriter.WriteAsync(httpContext.Response, _clientSession.CancellationToken);
60 | HttpNetTrace.Verbose(nameof(HttpSessionHandler), "Response '{0}' sent to '{1}'.", httpContext.Response.StatusCode, _clientSession.Client.Identifier);
61 | }
62 |
63 | if (httpContext.CloseConnection)
64 | {
65 | _clientSession.Close();
66 | }
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/HTTPnet.Core/Pipeline/HttpContextPipeline.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using HTTPnet.Core.Http;
5 |
6 | namespace HTTPnet.Core.Pipeline
7 | {
8 | public class HttpContextPipeline : IHttpRequestHandler
9 | {
10 | private readonly List _handlers = new List();
11 | private readonly IHttpContextPipelineExceptionHandler _exceptionHandler;
12 |
13 | public HttpContextPipeline(IHttpContextPipelineExceptionHandler exceptionHandler)
14 | {
15 | _exceptionHandler = exceptionHandler ?? throw new ArgumentNullException(nameof(exceptionHandler));
16 | }
17 |
18 | public async Task HandleHttpRequestAsync(HttpContext httpContext)
19 | {
20 | var pipelineContext = new HttpContextPipelineHandlerContext(httpContext);
21 | var offset = -1;
22 |
23 | try
24 | {
25 | foreach (var processor in _handlers)
26 | {
27 | await processor.ProcessRequestAsync(pipelineContext);
28 | offset++;
29 |
30 | if (pipelineContext.BreakPipeline)
31 | {
32 | break;
33 | }
34 | }
35 |
36 | pipelineContext.BreakPipeline = false;
37 |
38 | for (var i = offset; i >= 0; i--)
39 | {
40 | await _handlers[i].ProcessResponseAsync(pipelineContext);
41 | if (pipelineContext.BreakPipeline)
42 | {
43 | break;
44 | }
45 | }
46 | }
47 | catch (Exception exception)
48 | {
49 | await _exceptionHandler.HandleExceptionAsync(httpContext, exception);
50 | }
51 | }
52 |
53 | public void Add(IHttpContextPipelineHandler processor)
54 | {
55 | if (processor == null) throw new ArgumentNullException(nameof(processor));
56 |
57 | _handlers.Add(processor);
58 | }
59 |
60 | public void Insert(int index, IHttpContextPipelineHandler processor)
61 | {
62 | if (processor == null) throw new ArgumentNullException(nameof(processor));
63 |
64 | _handlers.Insert(index, processor);
65 | }
66 |
67 | public void InsertAfter(IHttpContextPipelineHandler processor) where TBefore : IHttpContextPipelineHandler
68 | {
69 | if (processor == null) throw new ArgumentNullException(nameof(processor));
70 |
71 | Insert(_handlers.FindIndex(h => h is TBefore) + 1, processor);
72 | }
73 |
74 | public void InsertBefore(IHttpContextPipelineHandler processor) where TAfter : IHttpContextPipelineHandler
75 | {
76 | Insert(_handlers.FindIndex(h => h is TAfter), processor);
77 | }
78 |
79 | public void Clear()
80 | {
81 | _handlers.Clear();
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.Core.Tests/WebSocketFrameTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using System.Threading;
5 | using HTTPnet.Core.WebSockets.Protocol;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace HTTPnet.Core.Tests
9 | {
10 | [TestClass]
11 | public class WebSocketFrameTests
12 | {
13 | [TestMethod]
14 | public void WebSocketFrame_Simple()
15 | {
16 | var payload = "{\r\n \"Hello\": \"World\"\r\n}";
17 | var payloadBuffer = Encoding.UTF8.GetBytes(payload);
18 | var webSocketFrame = new WebSocketFrame { Opcode = WebSocketOpcode.Binary, Payload = payloadBuffer };
19 |
20 | var memoryStream = new MemoryStream();
21 | new WebSocketFrameWriter(memoryStream).WriteAsync(webSocketFrame, CancellationToken.None).Wait();
22 | var result = memoryStream.ToArray();
23 |
24 | var expected = Convert.FromBase64String("ghh7DQogICJIZWxsbyI6ICJXb3JsZCINCn0=");
25 |
26 | CollectionAssert.AreEqual(expected, result);
27 | }
28 |
29 | [TestMethod]
30 | public void WebSocketFrame_LargePayload()
31 | {
32 | var payload = "{\r\n \"Hello12121212121212121212121212121212121212121212121212121AAAAAAAAAAAAAAA\": \"World56565656565656565656565656565656565656565656565BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCC\"\r\n}";
33 | var payloadBuffer = Encoding.UTF8.GetBytes(payload.ToString());
34 | var webSocketFrame = new WebSocketFrame { Opcode = WebSocketOpcode.Binary, Payload = payloadBuffer };
35 |
36 | var memoryStream = new MemoryStream();
37 | new WebSocketFrameWriter(memoryStream).WriteAsync(webSocketFrame, CancellationToken.None).Wait();
38 | var result = memoryStream.ToArray();
39 |
40 | var expected = Convert.FromBase64String("gn4As3sNCiAgIkhlbGxvMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjFBQUFBQUFBQUFBQUFBQUEiOiAiV29ybGQ1NjU2NTY1NjU2NTY1NjU2NTY1NjU2NTY1NjU2NTY1NjU2NTY1NjU2NTY1NjU2NUJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkNDQ0NDQ0NDQ0MiDQp9");
41 |
42 | CollectionAssert.AreEqual(expected, result);
43 | }
44 |
45 | [TestMethod]
46 | public void WebSocketFrame_Parse()
47 | {
48 | var payload = "{\"Hello\":\"World\"}";
49 | var payloadBuffer = Encoding.UTF8.GetBytes(payload);
50 | var sourceWebSocketFrame = new WebSocketFrame { Opcode = WebSocketOpcode.Binary, Payload = payloadBuffer };
51 | sourceWebSocketFrame.Opcode = WebSocketOpcode.Ping;
52 |
53 | var memoryStream = new MemoryStream();
54 | new WebSocketFrameWriter(memoryStream).WriteAsync(sourceWebSocketFrame, CancellationToken.None).Wait();
55 |
56 | memoryStream.Position = 0;
57 | var targetWebSocketFrame = new WebSocketFrameReader(memoryStream).ReadAsync(CancellationToken.None).Result;
58 |
59 | Assert.AreEqual(sourceWebSocketFrame.Fin, targetWebSocketFrame.Fin);
60 | Assert.AreEqual(sourceWebSocketFrame.Opcode, targetWebSocketFrame.Opcode);
61 | CollectionAssert.AreEqual(payloadBuffer, targetWebSocketFrame.Payload);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.Core.Tests/RawHttpRequestReaderTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using System.Threading;
4 | using HTTPnet.Core.Communication;
5 | using HTTPnet.Core.Http;
6 | using HTTPnet.Core.Http.Raw;
7 | using HTTPnet.Core.Pipeline;
8 | using HTTPnet.Core.Pipeline.Handlers;
9 | using Microsoft.VisualStudio.TestTools.UnitTesting;
10 |
11 | namespace HTTPnet.Core.Tests
12 | {
13 | [TestClass]
14 | public class RawHttpRequestReaderTests
15 | {
16 | [TestMethod]
17 | public void HttpRequestReader_ParseWithoutContentLength()
18 | {
19 | var buffer = new MemoryStream(Encoding.UTF8.GetBytes(GetRequestTextWithoutContentLength())) { Position = 0 };
20 | var parser = new RawHttpRequestReader(buffer, new HttpServerOptions());
21 |
22 | var request = parser.ReadAsync(CancellationToken.None).Result;
23 | Assert.IsTrue(request != null, "Parse failed.");
24 | Assert.AreEqual(HttpMethod.Delete, request.Method);
25 | Assert.AreEqual("/Uri%20/lalalo323/_/-/+/%/@/&/./~/:/#/;/,/*", request.Uri);
26 | Assert.AreEqual("Body123{}%!(:<>=", Encoding.UTF8.GetString(StreamToArray(request.Body)));
27 | Assert.AreEqual(HttpVersion.Version1_1, request.Version);
28 | Assert.AreEqual("localhost:2400", request.Headers["Host"]);
29 | Assert.AreEqual("keep-alive", request.Headers["Connection"]);
30 | Assert.AreEqual("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", request.Headers["Accept"]);
31 | Assert.AreEqual("1", request.Headers["Upgrade-Insecure-Requests"]);
32 | Assert.AreEqual("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36", request.Headers["User-Agent"]);
33 | Assert.AreEqual("gzip, deflate, sdch", request.Headers["Accept-Encoding"]);
34 | Assert.AreEqual("de,en-US;q=0.8,en;q=0.6,de-DE;q=0.4", request.Headers["Accept-Language"]);
35 | }
36 |
37 | [TestMethod]
38 | public void HttpRequestReader_ParseWithContentLength()
39 | {
40 | var buffer = new MemoryStream(Encoding.UTF8.GetBytes(GetRequestTextWithContentLength())) { Position = 0 };
41 | var parser = new RawHttpRequestReader(buffer, new HttpServerOptions());
42 |
43 | var request = parser.ReadAsync(CancellationToken.None).Result;
44 |
45 | var bodyReader = new RequestBodyHandler();
46 |
47 | Assert.IsTrue(request != null, "Parse failed.");
48 | Assert.AreEqual("Body123{}%!(:<>=", Encoding.UTF8.GetString(StreamToArray(request.Body)));
49 | }
50 |
51 | private static byte[] StreamToArray(Stream source)
52 | {
53 | var buffer = new byte[source.Length];
54 | source.Read(buffer, 0, buffer.Length);
55 |
56 | return buffer;
57 | }
58 |
59 | private static string GetRequestTextWithContentLength()
60 | {
61 | return @"GET /Uri%20/lalalo323/_/-/+/%/@/&/./~/:/#/;/,/* HTTP/1.1
62 | Host: localhost:2400
63 | Content-Length: 16
64 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
65 | Upgrade-Insecure-Requests: 1
66 |
67 | Body123{}%!(:<>=";
68 | }
69 |
70 | private static string GetRequestTextWithoutContentLength()
71 | {
72 | return @"DELETE /Uri%20/lalalo323/_/-/+/%/@/&/./~/:/#/;/,/* HTTP/1.1
73 | Host: localhost:2400
74 | Connection: keep-alive
75 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
76 | Upgrade-Insecure-Requests: 1
77 | User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
78 | Accept-Encoding: gzip, deflate, sdch
79 | Accept-Language: de,en-US;q=0.8,en;q=0.6,de-DE;q=0.4
80 |
81 | Body123{}%!(:<>=";
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/Protocol/WebSocketFrameReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace HTTPnet.Core.WebSockets.Protocol
7 | {
8 | public class WebSocketFrameReader
9 | {
10 | private readonly Stream _receiveStream;
11 |
12 | public WebSocketFrameReader(Stream receiveStream)
13 | {
14 | _receiveStream = receiveStream ?? throw new ArgumentNullException(nameof(receiveStream));
15 | }
16 |
17 | public async Task ReadAsync(CancellationToken cancellationToken)
18 | {
19 | // https://tools.ietf.org/html/rfc6455
20 |
21 | var webSocketFrame = new WebSocketFrame();
22 |
23 | var buffer = await ReadBytesAsync(2, cancellationToken);
24 | var byte0 = buffer[0];
25 | var byte1 = buffer[1];
26 |
27 | if ((byte0 & 128) == 128)
28 | {
29 | webSocketFrame.Fin = true;
30 | byte0 = (byte)(127 & byte0);
31 | }
32 |
33 | webSocketFrame.Opcode = (WebSocketOpcode)byte0;
34 |
35 | var hasMask = (byte1 & 128) == 128;
36 | var maskingKey = new byte[4];
37 |
38 | var payloadLength = byte1 & 127;
39 | if (payloadLength == 126)
40 | {
41 | // The length is 7 + 16 bits.
42 | buffer = await ReadBytesAsync(2, cancellationToken);
43 | var byte2 = buffer[0];
44 | var byte3 = buffer[1];
45 |
46 | payloadLength = byte3 | byte2 >> 8 | 126 >> 16;
47 | }
48 | else if (payloadLength == 127)
49 | {
50 | // The length is 7 + 64 bits.
51 | buffer = await ReadBytesAsync(8, cancellationToken);
52 | var byte2 = buffer[0];
53 | var byte3 = buffer[1];
54 | var byte4 = buffer[2];
55 | var byte5 = buffer[3];
56 | var byte6 = buffer[4];
57 | var byte7 = buffer[5];
58 | var byte8 = buffer[6];
59 | var byte9 = buffer[7];
60 |
61 | payloadLength = byte9 | byte8 >> 56 | byte7 >> 48 | byte6 >> 40 | byte5 >> 32 | byte4 >> 24 | byte3 >> 16 | byte2 >> 8 | 127;
62 | }
63 |
64 | if (hasMask)
65 | {
66 | buffer = await ReadBytesAsync(4, cancellationToken);
67 | maskingKey[0] = buffer[0];
68 | maskingKey[1] = buffer[1];
69 | maskingKey[2] = buffer[2];
70 | maskingKey[3] = buffer[3];
71 | }
72 |
73 | webSocketFrame.MaskingKey = BitConverter.ToUInt32(maskingKey, 0);
74 |
75 | webSocketFrame.Payload = new byte[payloadLength];
76 | if (payloadLength > 0)
77 | {
78 | await _receiveStream.ReadAsync(webSocketFrame.Payload, 0, webSocketFrame.Payload.Length, cancellationToken).ConfigureAwait(false);
79 | }
80 |
81 | if (hasMask)
82 | {
83 | for (var i = 0; i < webSocketFrame.Payload.Length; i++)
84 | {
85 | webSocketFrame.Payload[i] = (byte)(webSocketFrame.Payload[i] ^ maskingKey[i % 4]);
86 | }
87 | }
88 |
89 | return webSocketFrame;
90 | }
91 |
92 | private async Task ReadBytesAsync(int count, CancellationToken cancellationToken)
93 | {
94 | var buffer = new byte[count];
95 |
96 | var effectiveCount = await _receiveStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
97 | if (effectiveCount == 0 || effectiveCount != count)
98 | {
99 | throw new TaskCanceledException();
100 | }
101 |
102 | return buffer;
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/HTTPnet.Core/Http/Raw/RawHttpRequestReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using HTTPnet.Core.Exceptions;
8 |
9 | namespace HTTPnet.Core.Http.Raw
10 | {
11 | public sealed class RawHttpRequestReader
12 | {
13 | private readonly Stream _receiveStream;
14 | private readonly Queue _buffer = new Queue();
15 | private readonly byte[] _chunkBuffer;
16 |
17 | public RawHttpRequestReader(Stream receiveStream, HttpServerOptions options)
18 | {
19 | _receiveStream = receiveStream ?? throw new ArgumentNullException(nameof(receiveStream));
20 | if (options == null) throw new ArgumentNullException(nameof(options));
21 |
22 | _chunkBuffer = new byte[options.ReceiveChunkSize];
23 | }
24 |
25 | public int BufferLength => _buffer.Count;
26 |
27 | public async Task ReadAsync(CancellationToken cancellationToken)
28 | {
29 | await FetchChunk(cancellationToken).ConfigureAwait(false);
30 |
31 | var statusLine = ReadLine();
32 | var statusLineItems = statusLine.Split(' ');
33 |
34 | if (statusLineItems.Length != 3)
35 | {
36 | throw new HttpRequestInvalidException();
37 | }
38 |
39 | var request = new RawHttpRequest
40 | {
41 | Method = statusLineItems[0].ToUpperInvariant(),
42 | Uri = statusLineItems[1],
43 | Version = statusLineItems[2].ToUpperInvariant(),
44 | Headers = ParseHeaders()
45 | };
46 |
47 | return request;
48 | }
49 |
50 | public async Task FetchChunk(CancellationToken cancellationToken)
51 | {
52 | var size = await _receiveStream.ReadAsync(_chunkBuffer, 0, _chunkBuffer.Length, cancellationToken);
53 | if (size == 0)
54 | {
55 | throw new TaskCanceledException();
56 | }
57 |
58 | for (var i = 0; i < size; i++)
59 | {
60 | _buffer.Enqueue(_chunkBuffer[i]);
61 | }
62 | }
63 |
64 | public byte DequeueFromBuffer()
65 | {
66 | return _buffer.Dequeue();
67 | }
68 |
69 | private Dictionary ParseHeaders()
70 | {
71 | var headers = new Dictionary();
72 |
73 | var line = ReadLine();
74 | while (!string.IsNullOrEmpty(line))
75 | {
76 | var header = ParseHeader(line);
77 | headers.Add(header.Key, header.Value);
78 |
79 | line = ReadLine();
80 | }
81 |
82 | return headers;
83 | }
84 |
85 | private static KeyValuePair ParseHeader(string source)
86 | {
87 | var delimiterIndex = source.IndexOf(':');
88 | if (delimiterIndex == -1)
89 | {
90 | return new KeyValuePair(source, null);
91 | }
92 |
93 | var name = source.Substring(0, delimiterIndex).Trim();
94 | var value = source.Substring(delimiterIndex + 1).Trim();
95 |
96 | return new KeyValuePair(name, value);
97 | }
98 |
99 | private string ReadLine()
100 | {
101 | var buffer = new StringBuilder();
102 |
103 | while (_buffer.Count > 0)
104 | {
105 | var @char = (char)_buffer.Dequeue();
106 |
107 | if (@char == '\r')
108 | {
109 | @char = (char)_buffer.Dequeue();
110 | if (@char != '\n')
111 | {
112 | throw new HttpRequestInvalidException();
113 | }
114 |
115 | return buffer.ToString();
116 | }
117 |
118 | buffer.Append(@char);
119 | }
120 |
121 | throw new HttpRequestInvalidException();
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/Tests/HTTPnet.TestApp.NetFramework/Program.cs:
--------------------------------------------------------------------------------
1 | using HTTPnet.Core.Diagnostics;
2 | using HTTPnet.Core.Http;
3 | using HTTPnet.Core.Pipeline;
4 | using HTTPnet.Core.Pipeline.Handlers;
5 | using HTTPnet.Core.WebSockets;
6 | using System;
7 | using System.Globalization;
8 | using System.IO;
9 | using System.Net;
10 | using System.Security.Cryptography;
11 | using System.Text;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace HTTPnet.TestApp.NetFramework
16 | {
17 | public static class Program
18 | {
19 | public static void Main(string[] args)
20 | {
21 | HttpNetTrace.TraceMessagePublished += (s, e) => Console.WriteLine("[" + e.Source + "] [" + e.Level + "] [" + e.Message + "] [" + e.Exception + "]");
22 |
23 | var pipeline = new HttpContextPipeline(new SimpleExceptionHandler());
24 | pipeline.Add(new RequestBodyHandler());
25 | pipeline.Add(new TraceHandler());
26 | pipeline.Add(new WebSocketRequestHandler(ComputeSha1Hash, SessionCreated));
27 | pipeline.Add(new ResponseBodyLengthHandler());
28 | pipeline.Add(new ResponseCompressionHandler());
29 | pipeline.Add(new SimpleHttpRequestHandler());
30 |
31 | var httpServer = new HttpServerFactory().CreateHttpServer();
32 | httpServer.RequestHandler = pipeline;
33 | httpServer.StartAsync(HttpServerOptions.Default).GetAwaiter().GetResult();
34 |
35 |
36 | Thread.Sleep(Timeout.Infinite);
37 | }
38 |
39 | private static void SessionCreated(WebSocketSession webSocketSession)
40 | {
41 | webSocketSession.MessageReceived += async (s, e) =>
42 | {
43 | Console.WriteLine(((WebSocketTextMessage)e.Message).Text);
44 |
45 | await webSocketSession.SendAsync("Reply...");
46 | };
47 | }
48 |
49 | private static byte[] ComputeSha1Hash(byte[] source)
50 | {
51 | using (var sha1 = SHA1.Create())
52 | {
53 | return sha1.ComputeHash(source);
54 | }
55 | }
56 |
57 | public class SimpleExceptionHandler : IHttpContextPipelineExceptionHandler
58 | {
59 | public Task HandleExceptionAsync(HttpContext httpContext, Exception exception)
60 | {
61 | httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
62 |
63 | httpContext.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(exception.ToString()));
64 | httpContext.Response.Headers[HttpHeader.ContentLength] = httpContext.Response.Body.Length.ToString(CultureInfo.InvariantCulture);
65 |
66 | httpContext.CloseConnection = true;
67 |
68 | return Task.FromResult(0);
69 | }
70 | }
71 |
72 | public class SimpleHttpRequestHandler : IHttpContextPipelineHandler
73 | {
74 | public Task ProcessRequestAsync(HttpContextPipelineHandlerContext context)
75 | {
76 | if (context.HttpContext.Request.Uri.Equals("/404test"))
77 | {
78 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
79 | return Task.FromResult(0);
80 | }
81 |
82 | if (context.HttpContext.Request.Method.Equals(HttpMethod.Post) && context.HttpContext.Request.Uri.Equals("/toUpper"))
83 | {
84 | var s = new StreamReader(context.HttpContext.Request.Body).ReadToEnd();
85 | context.HttpContext.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(s.ToUpperInvariant()));
86 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK; // OK is also default
87 | return Task.FromResult(0);
88 | }
89 |
90 | var filename = "C:" + context.HttpContext.Request.Uri.Replace("/", "\\");
91 | if (File.Exists(filename))
92 | {
93 | // Return a file from the filesystem.
94 | context.HttpContext.Response.Body = File.OpenRead(filename);
95 | }
96 | else
97 | {
98 | // Return a static text.
99 | context.HttpContext.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes("Hello World"));
100 | }
101 |
102 | return Task.FromResult(0);
103 | }
104 |
105 | public Task ProcessResponseAsync(HttpContextPipelineHandlerContext context)
106 | {
107 | return Task.FromResult(0);
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/HTTPnet.Core/HttpServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using HTTPnet.Core.Communication;
5 | using HTTPnet.Core.Diagnostics;
6 | using HTTPnet.Core.Http;
7 |
8 | namespace HTTPnet.Core
9 | {
10 | public sealed class HttpServer : IHttpServer
11 | {
12 | private readonly Func _socketWrapperFactory;
13 |
14 | private IServerSocketWrapper _socketWrapper;
15 | private HttpServerOptions _options;
16 | private CancellationTokenSource _cancellationTokenSource;
17 |
18 | public HttpServer(Func socketWrapperFactory)
19 | {
20 | _socketWrapperFactory = socketWrapperFactory ?? throw new ArgumentNullException(nameof(socketWrapperFactory));
21 | }
22 |
23 | public IHttpRequestHandler RequestHandler { get; set; }
24 |
25 | public async Task StartAsync(HttpServerOptions options)
26 | {
27 | _options = options ?? throw new ArgumentNullException(nameof(options));
28 | if (RequestHandler == null) throw new InvalidOperationException("RequestHandler is not set.");
29 |
30 | try
31 | {
32 | _cancellationTokenSource = new CancellationTokenSource();
33 | _socketWrapper = _socketWrapperFactory(options);
34 |
35 | await _socketWrapper.StartAsync();
36 |
37 | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
38 | Task.Factory.StartNew(() => AcceptConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
39 | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
40 | }
41 | catch (Exception)
42 | {
43 | await StopAsync();
44 | throw;
45 | }
46 | }
47 |
48 | public async Task StopAsync()
49 | {
50 | if (_socketWrapper != null)
51 | {
52 | await _socketWrapper.StopAsync();
53 | }
54 |
55 | _socketWrapper?.Dispose();
56 | _socketWrapper = null;
57 | }
58 |
59 | private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
60 | {
61 | try
62 | {
63 | while (!cancellationToken.IsCancellationRequested)
64 | {
65 | var client = await _socketWrapper.AcceptAsync().ConfigureAwait(false);
66 | HttpNetTrace.Information(nameof(HttpServer), "Client '{0}' connected.", client.Identifier);
67 |
68 | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
69 | Task.Run(async () => await HandleClientConnectionAsync(client).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
70 | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
71 | }
72 | }
73 | catch (OperationCanceledException)
74 | {
75 | }
76 | catch (Exception exception)
77 | {
78 | HttpNetTrace.Error(nameof(HttpServer), exception, "Unhandled exception while accepting clients.");
79 | }
80 | finally
81 | {
82 | _cancellationTokenSource?.Cancel(false);
83 | }
84 | }
85 |
86 | public void Dispose()
87 | {
88 | _cancellationTokenSource?.Cancel(false);
89 | _cancellationTokenSource?.Dispose();
90 | _cancellationTokenSource = null;
91 |
92 | _socketWrapper?.Dispose();
93 | }
94 |
95 | private async Task HandleClientConnectionAsync(IClientSocketWrapper client)
96 | {
97 | using (var clientSession = new ClientSession(client, this, _options))
98 | {
99 | try
100 | {
101 | await clientSession.RunAsync().ConfigureAwait(false);
102 | }
103 | catch (OperationCanceledException)
104 | {
105 | }
106 | catch (Exception exception)
107 | {
108 | HttpNetTrace.Error(nameof(HttpServer), exception, "Unhandled exception while handling cient connection.");
109 | }
110 | finally
111 | {
112 | HttpNetTrace.Information(nameof(HttpServer), "Client '{0}' disconnected.", client.Identifier);
113 | await client.DisconnectAsync();
114 | }
115 | }
116 | }
117 |
118 | internal async Task HandleHttpRequestAsync(HttpContext httpContext)
119 | {
120 | try
121 | {
122 | var handler = RequestHandler;
123 | if (handler == null)
124 | {
125 | return;
126 | }
127 |
128 | await RequestHandler.HandleHttpRequestAsync(httpContext);
129 | }
130 | catch (Exception exception)
131 | {
132 | HttpNetTrace.Error(nameof(HttpServer), exception, "Unhandled exception while handling received HTTP request.");
133 | }
134 | }
135 | }
136 | }
--------------------------------------------------------------------------------
/HTTPnet.Core/WebSockets/WebSocketSession.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using HTTPnet.Core.Communication;
7 | using HTTPnet.Core.WebSockets.Protocol;
8 |
9 | namespace HTTPnet.Core.WebSockets
10 | {
11 | public class WebSocketSession : ISessionHandler
12 | {
13 | private readonly List _frameQueue = new List();
14 | private readonly WebSocketFrameWriter _webSocketFrameWriter;
15 | private readonly ClientSession _clientSession;
16 |
17 | public WebSocketSession(ClientSession clientSession)
18 | {
19 | _clientSession = clientSession ?? throw new ArgumentNullException(nameof(clientSession));
20 |
21 | _webSocketFrameWriter = new WebSocketFrameWriter(_clientSession.Client.SendStream);
22 | }
23 |
24 | public event EventHandler MessageReceived;
25 |
26 | public event EventHandler Closed;
27 |
28 | public async Task ProcessAsync()
29 | {
30 | var webSocketFrame = await new WebSocketFrameReader(_clientSession.Client.ReceiveStream).ReadAsync(_clientSession.CancellationToken).ConfigureAwait(false);
31 | switch (webSocketFrame.Opcode)
32 | {
33 | case WebSocketOpcode.Ping:
34 | {
35 | webSocketFrame.Opcode = WebSocketOpcode.Pong;
36 | await _webSocketFrameWriter.WriteAsync(webSocketFrame, _clientSession.CancellationToken).ConfigureAwait(false);
37 | return;
38 | }
39 |
40 | case WebSocketOpcode.ConnectionClose:
41 | {
42 | await CloseAsync().ConfigureAwait(false);
43 | return;
44 | }
45 |
46 | case WebSocketOpcode.Pong:
47 | {
48 | return;
49 | }
50 | }
51 |
52 | _frameQueue.Add(webSocketFrame);
53 |
54 | if (webSocketFrame.Fin)
55 | {
56 | var message = GenerateMessage();
57 | _frameQueue.Clear();
58 |
59 | MessageReceived?.Invoke(this, new WebSocketMessageReceivedEventArgs(message, this));
60 | }
61 | }
62 |
63 | public Task CloseAsync()
64 | {
65 | _clientSession.Close();
66 | Closed?.Invoke(this, EventArgs.Empty);
67 |
68 | return Task.FromResult(0);
69 | }
70 |
71 | public async Task SendAsync(string text)
72 | {
73 | if (text == null) throw new ArgumentNullException(nameof(text));
74 |
75 | await _webSocketFrameWriter.WriteAsync(new WebSocketFrame
76 | {
77 | Opcode = WebSocketOpcode.Text,
78 | Payload = Encoding.UTF8.GetBytes(text)
79 | }, _clientSession.CancellationToken).ConfigureAwait(false);
80 | }
81 |
82 | public async Task SendAsync(byte[] data)
83 | {
84 | if (data == null) throw new ArgumentNullException(nameof(data));
85 |
86 | await _webSocketFrameWriter.WriteAsync(new WebSocketFrame
87 | {
88 | Opcode = WebSocketOpcode.Binary,
89 | Payload = data
90 | }, _clientSession.CancellationToken).ConfigureAwait(false);
91 | }
92 |
93 | private WebSocketMessage GenerateMessage()
94 | {
95 | ValidateFrameQueue();
96 |
97 | var buffer = new List();
98 | foreach (var frame in _frameQueue)
99 | {
100 | buffer.AddRange(frame.Payload);
101 | }
102 |
103 | var messageType = _frameQueue.First().Opcode;
104 |
105 | if (messageType == WebSocketOpcode.Text)
106 | {
107 | var text = Encoding.UTF8.GetString(buffer.ToArray(), 0, buffer.Count);
108 | return new WebSocketTextMessage(text);
109 | }
110 |
111 | if (messageType == WebSocketOpcode.Binary)
112 | {
113 | return new WebSocketBinaryMessage(buffer.ToArray());
114 | }
115 |
116 | throw new NotSupportedException();
117 | }
118 |
119 | private void ValidateFrameQueue()
120 | {
121 | // Details: https://tools.ietf.org/html/rfc6455#section-5.6 PAGE 34
122 | if (!_frameQueue.Last().Fin)
123 | {
124 | throw new InvalidOperationException("Fragmented frames are invalid.");
125 | }
126 |
127 | if (_frameQueue.First().Opcode != WebSocketOpcode.Binary &&
128 | _frameQueue.First().Opcode != WebSocketOpcode.Text)
129 | {
130 | throw new InvalidOperationException("Frame opcode is invalid.");
131 | }
132 |
133 | if (_frameQueue.Count > 2)
134 | {
135 | for (int i = 1; i < _frameQueue.Count - 1; i++)
136 | {
137 | if (_frameQueue[i].Opcode != WebSocketOpcode.Continuation)
138 | {
139 | throw new InvalidOperationException("Fragmented frame is invalid.");
140 | }
141 |
142 | if (_frameQueue[i].Fin)
143 | {
144 | throw new InvalidOperationException("Fragmented frame is invalid.");
145 | }
146 | }
147 | }
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/Tests/HTTPnet.Core.Tests/HTTPnet.Core.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Debug
8 | AnyCPU
9 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}
10 | Library
11 | Properties
12 | HTTPnet.Core.Tests
13 | HTTPnet.Core.Tests
14 | v4.6.1
15 | 512
16 |
17 |
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 | ..\..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
39 |
40 |
41 | ..\..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | {26939955-72A0-41F3-A68C-F04DEF24E326}
64 | HTTPnet.Core
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/.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 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
290 |
291 | Build/nuget.exe
292 |
--------------------------------------------------------------------------------
/Frameworks/HTTPnet.UniversalWindows/HTTPnet.UniversalWindows.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7F642535-8928-4322-9F84-4D5E16462817}
8 | Library
9 | Properties
10 | HTTPnet
11 | HTTPnet.UniversalWindows
12 | en-US
13 | UAP
14 | 10.0.15063.0
15 | 10.0.10240.0
16 | 14
17 | 512
18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
19 |
20 |
21 | AnyCPU
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
27 | prompt
28 | 4
29 |
30 |
31 | AnyCPU
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE;NETFX_CORE;WINDOWS_UWP
36 | prompt
37 | 4
38 |
39 |
40 | x86
41 | true
42 | bin\x86\Debug\
43 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
44 | ;2008
45 | full
46 | x86
47 | false
48 | prompt
49 |
50 |
51 | x86
52 | bin\x86\Release\
53 | TRACE;NETFX_CORE;WINDOWS_UWP
54 | true
55 | ;2008
56 | pdbonly
57 | x86
58 | false
59 | prompt
60 |
61 |
62 | ARM
63 | true
64 | bin\ARM\Debug\
65 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
66 | ;2008
67 | full
68 | ARM
69 | false
70 | prompt
71 |
72 |
73 | ARM
74 | bin\ARM\Release\
75 | TRACE;NETFX_CORE;WINDOWS_UWP
76 | true
77 | ;2008
78 | pdbonly
79 | ARM
80 | false
81 | prompt
82 |
83 |
84 | x64
85 | true
86 | bin\x64\Debug\
87 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
88 | ;2008
89 | full
90 | x64
91 | false
92 | prompt
93 |
94 |
95 | x64
96 | bin\x64\Release\
97 | TRACE;NETFX_CORE;WINDOWS_UWP
98 | true
99 | ;2008
100 | pdbonly
101 | x64
102 | false
103 | prompt
104 |
105 |
106 | PackageReference
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | 5.4.0
118 |
119 |
120 |
121 |
122 |
123 | {26939955-72A0-41F3-A68C-F04DEF24E326}
124 | HTTPnet.Core
125 |
126 |
127 |
128 | 14.0
129 |
130 |
131 |
138 |
--------------------------------------------------------------------------------
/HTTPnet.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPnet.Core", "HTTPnet.Core\HTTPnet.Core.csproj", "{26939955-72A0-41F3-A68C-F04DEF24E326}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{10EF0BF3-2E6F-419C-B0FD-9A3018C832F3}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPnet.TestApp.NetFramework", "Tests\HTTPnet.TestApp.NetFramework\HTTPnet.TestApp.NetFramework.csproj", "{D65CA58A-3B9E-41BB-826E-39510354F7A5}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{EEE9FCC4-6821-4AC4-81D0-64A52E9418EF}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPnet.NetFramework", "Frameworks\HTTPnet.NetFramework\HTTPnet.NetFramework.csproj", "{FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPnet.UniversalWindows", "Frameworks\HTTPnet.UniversalWindows\HTTPnet.UniversalWindows.csproj", "{7F642535-8928-4322-9F84-4D5E16462817}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6C84710B-6A7A-4B39-98AD-E44D10D13EC2}"
19 | ProjectSection(SolutionItems) = preProject
20 | README.md = README.md
21 | EndProjectSection
22 | EndProject
23 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{37B419E7-7B1F-4208-A88E-64AB55D69D8D}"
24 | ProjectSection(SolutionItems) = preProject
25 | Build\build.ps1 = Build\build.ps1
26 | Build\HTTPnet.nuspec = Build\HTTPnet.nuspec
27 | EndProjectSection
28 | EndProject
29 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPnet.NetStandard", "Frameworks\HTTPnet.NetStandard\HTTPnet.NetStandard.csproj", "{395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}"
30 | EndProject
31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPnet.Core.Tests", "Tests\HTTPnet.Core.Tests\HTTPnet.Core.Tests.csproj", "{59FFE02A-60A3-4762-B2E5-3B4E44939101}"
32 | EndProject
33 | Global
34 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
35 | Debug|Any CPU = Debug|Any CPU
36 | Debug|ARM = Debug|ARM
37 | Debug|x64 = Debug|x64
38 | Debug|x86 = Debug|x86
39 | Release|Any CPU = Release|Any CPU
40 | Release|ARM = Release|ARM
41 | Release|x64 = Release|x64
42 | Release|x86 = Release|x86
43 | EndGlobalSection
44 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
45 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|ARM.ActiveCfg = Debug|Any CPU
48 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|ARM.Build.0 = Debug|Any CPU
49 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|x64.ActiveCfg = Debug|Any CPU
50 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|x64.Build.0 = Debug|Any CPU
51 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|x86.ActiveCfg = Debug|Any CPU
52 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Debug|x86.Build.0 = Debug|Any CPU
53 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|Any CPU.Build.0 = Release|Any CPU
55 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|ARM.ActiveCfg = Release|Any CPU
56 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|ARM.Build.0 = Release|Any CPU
57 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|x64.ActiveCfg = Release|Any CPU
58 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|x64.Build.0 = Release|Any CPU
59 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|x86.ActiveCfg = Release|Any CPU
60 | {26939955-72A0-41F3-A68C-F04DEF24E326}.Release|x86.Build.0 = Release|Any CPU
61 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|ARM.ActiveCfg = Debug|Any CPU
64 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|ARM.Build.0 = Debug|Any CPU
65 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|x64.ActiveCfg = Debug|Any CPU
66 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|x64.Build.0 = Debug|Any CPU
67 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|x86.ActiveCfg = Debug|Any CPU
68 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Debug|x86.Build.0 = Debug|Any CPU
69 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|Any CPU.Build.0 = Release|Any CPU
71 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|ARM.ActiveCfg = Release|Any CPU
72 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|ARM.Build.0 = Release|Any CPU
73 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|x64.ActiveCfg = Release|Any CPU
74 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|x64.Build.0 = Release|Any CPU
75 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|x86.ActiveCfg = Release|Any CPU
76 | {D65CA58A-3B9E-41BB-826E-39510354F7A5}.Release|x86.Build.0 = Release|Any CPU
77 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|Any CPU.Build.0 = Debug|Any CPU
79 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|ARM.ActiveCfg = Debug|Any CPU
80 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|ARM.Build.0 = Debug|Any CPU
81 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|x64.ActiveCfg = Debug|Any CPU
82 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|x64.Build.0 = Debug|Any CPU
83 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|x86.ActiveCfg = Debug|Any CPU
84 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Debug|x86.Build.0 = Debug|Any CPU
85 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|Any CPU.ActiveCfg = Release|Any CPU
86 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|Any CPU.Build.0 = Release|Any CPU
87 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|ARM.ActiveCfg = Release|Any CPU
88 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|ARM.Build.0 = Release|Any CPU
89 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|x64.ActiveCfg = Release|Any CPU
90 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|x64.Build.0 = Release|Any CPU
91 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|x86.ActiveCfg = Release|Any CPU
92 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B}.Release|x86.Build.0 = Release|Any CPU
93 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
94 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|Any CPU.Build.0 = Debug|Any CPU
95 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|ARM.ActiveCfg = Debug|ARM
96 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|ARM.Build.0 = Debug|ARM
97 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|x64.ActiveCfg = Debug|x64
98 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|x64.Build.0 = Debug|x64
99 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|x86.ActiveCfg = Debug|x86
100 | {7F642535-8928-4322-9F84-4D5E16462817}.Debug|x86.Build.0 = Debug|x86
101 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|Any CPU.ActiveCfg = Release|Any CPU
102 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|Any CPU.Build.0 = Release|Any CPU
103 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|ARM.ActiveCfg = Release|ARM
104 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|ARM.Build.0 = Release|ARM
105 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|x64.ActiveCfg = Release|x64
106 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|x64.Build.0 = Release|x64
107 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|x86.ActiveCfg = Release|x86
108 | {7F642535-8928-4322-9F84-4D5E16462817}.Release|x86.Build.0 = Release|x86
109 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
110 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|Any CPU.Build.0 = Debug|Any CPU
111 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|ARM.ActiveCfg = Debug|Any CPU
112 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|ARM.Build.0 = Debug|Any CPU
113 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|x64.ActiveCfg = Debug|Any CPU
114 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|x64.Build.0 = Debug|Any CPU
115 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|x86.ActiveCfg = Debug|Any CPU
116 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Debug|x86.Build.0 = Debug|Any CPU
117 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|Any CPU.ActiveCfg = Release|Any CPU
118 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|Any CPU.Build.0 = Release|Any CPU
119 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|ARM.ActiveCfg = Release|Any CPU
120 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|ARM.Build.0 = Release|Any CPU
121 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|x64.ActiveCfg = Release|Any CPU
122 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|x64.Build.0 = Release|Any CPU
123 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|x86.ActiveCfg = Release|Any CPU
124 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71}.Release|x86.Build.0 = Release|Any CPU
125 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
126 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|Any CPU.Build.0 = Debug|Any CPU
127 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|ARM.ActiveCfg = Debug|Any CPU
128 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|ARM.Build.0 = Debug|Any CPU
129 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|x64.ActiveCfg = Debug|Any CPU
130 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|x64.Build.0 = Debug|Any CPU
131 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|x86.ActiveCfg = Debug|Any CPU
132 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Debug|x86.Build.0 = Debug|Any CPU
133 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|Any CPU.ActiveCfg = Release|Any CPU
134 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|Any CPU.Build.0 = Release|Any CPU
135 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|ARM.ActiveCfg = Release|Any CPU
136 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|ARM.Build.0 = Release|Any CPU
137 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|x64.ActiveCfg = Release|Any CPU
138 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|x64.Build.0 = Release|Any CPU
139 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|x86.ActiveCfg = Release|Any CPU
140 | {59FFE02A-60A3-4762-B2E5-3B4E44939101}.Release|x86.Build.0 = Release|Any CPU
141 | EndGlobalSection
142 | GlobalSection(SolutionProperties) = preSolution
143 | HideSolutionNode = FALSE
144 | EndGlobalSection
145 | GlobalSection(NestedProjects) = preSolution
146 | {D65CA58A-3B9E-41BB-826E-39510354F7A5} = {10EF0BF3-2E6F-419C-B0FD-9A3018C832F3}
147 | {FD9DAEFC-98DE-43C6-AE07-EC60D2BCB78B} = {EEE9FCC4-6821-4AC4-81D0-64A52E9418EF}
148 | {7F642535-8928-4322-9F84-4D5E16462817} = {EEE9FCC4-6821-4AC4-81D0-64A52E9418EF}
149 | {395D8AD4-D81F-41F8-B3EE-C725BA4A8C71} = {EEE9FCC4-6821-4AC4-81D0-64A52E9418EF}
150 | {59FFE02A-60A3-4762-B2E5-3B4E44939101} = {10EF0BF3-2E6F-419C-B0FD-9A3018C832F3}
151 | EndGlobalSection
152 | GlobalSection(ExtensibilityGlobals) = postSolution
153 | SolutionGuid = {D6F8FA0A-D22B-47B6-880B-0D4A325EC669}
154 | EndGlobalSection
155 | EndGlobal
156 |
--------------------------------------------------------------------------------