Feature: Implement WebSocket transport (#86)

* Feature: Implement WebSocket transport

* Refactor: Remove unused `Debug.Log` calls

* Feature: Directly use websocket-sharp sources

* Document: Add the relevant documentation for websocket-sharp library
This commit is contained in:
Nathanael Demacon 2021-08-06 17:26:25 +02:00 коммит произвёл GitHub
Родитель 367a0bfa65
Коммит cc72630721
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
204 изменённых файлов: 36358 добавлений и 11 удалений

5
.gitignore поставляемый
Просмотреть файл

@ -263,4 +263,7 @@ __pycache__/
docs/LastBuild.log
# Unity Package Manager CI output
upm-ci~
upm-ci~
# .DS_Store files
.DS_Store

Просмотреть файл

@ -27,7 +27,7 @@ Check our [contribution guidelines](CONTRIBUTING.md) for information on how to c
|**[Enet](/Transports/com.mlapi.contrib.transport.enet)**| Desktop, Mobile\* | |:heavy_check_mark: | :heavy_check_mark: |
|**[LiteNetLib](/Transports/com.mlapi.contrib.transport.litenetlib)**| Desktop, Mobile | | :heavy_check_mark: | :heavy_check_mark: |
|**[SteamP2P](/Transports/com.mlapi.contrib.transport.steamp2p)**| Steam || :heavy_check_mark: | :heavy_check_mark: |
|**WebSocket**| Desktop, Mobile, WebGL | | :x:| :x: |
|**[WebSocket](/Transports/com.mlapi.contrib.transport.websocket)**| Desktop, Mobile, WebGL | | :heavy_check_mark:| :question: |
|**[Photon Realtime](/Transports/com.mlapi.contrib.transport.photon-realtime)**| Desktop, Mobile, WebGL\** || :heavy_check_mark: | |
|**[Facepunch](/Transports/com.mlapi.contrib.transport.facepunch)**| Steam || :heavy_check_mark: | :heavy_check_mark: |

Просмотреть файл

@ -164,4 +164,36 @@ TO AS PART OF CREATING AN ACCOUNT WITH EXIT GAMES, INC.
5. PLEASE NOTE THAT YOU MUST USE THE APP ID UNDER YOUR EXIST GAMES ACCOUNT WHEN MAKING
USE OF THE MATERIALS IN THIS FILE.
---------
---------
## Package: Transports/com.mlapi.contrib.transport.websocket
---------
Component Name: websocket-sharp
License Type: MIT
Copyright (c) 2010-2021 sta.blockhead
https://github.com/sta/websocket-sharp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------

Просмотреть файл

@ -0,0 +1,5 @@
# Changelog
All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
## 1.0.0
First version of the WebSocket transport as a Unity package.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0a024bb1894e44e4b84f176b0b386f24
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2021 Unity Technologies
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 56fa6d8e6a036b74eaef27818e3698c4
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1 @@
WebSocket transport for MLAPI.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8ffe30828b308c245b69db7e4a4787d8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d8bfbe123de7f4140b6b2071b1a7cbfa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using WebSocketSharp;
namespace MLAPI.Transports.WebSocket
{
public interface IWebSocketClient
{
Queue<WebSocketEvent> EventQueue { get; }
ulong WaitTime { get; }
WebSocketState ReadyState { get; }
void Connect();
void Close(CloseStatusCode code = CloseStatusCode.Normal, string reason = null);
void Send(ArraySegment<byte> data);
WebSocketEvent Poll();
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 804d38b60ad2641019d447cff99a8d62
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,112 @@
#if UNITY_WEBGL
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using WebSocketSharp;
namespace MLAPI.Transports.WebSocket
{
public class JSWebSocketClient : IWebSocketClient
{
public Queue<WebSocketEvent> EventQueue { get; } = new Queue<WebSocketEvent>();
[DllImport("__Internal")]
internal static extern void _Connect();
[DllImport("__Internal")]
internal static extern void _Close(CloseStatusCode code = CloseStatusCode.Normal, string reason = null);
[DllImport("__Internal")]
internal static extern void _Send(byte[] data, int offset, int count);
[DllImport("__Internal")]
internal static extern WebSocketState _GetState();
public ulong WaitTime => 0;
public WebSocketState ReadyState => _GetState();
public void Connect()
{
_Connect();
}
public void Close(CloseStatusCode code = CloseStatusCode.Normal, string reason = null)
{
_Close(code, reason);
}
public void Send(ArraySegment<byte> data)
{
_Send(data.Array, data.Offset, data.Count);
}
public WebSocketEvent Poll()
{
if (EventQueue.Count > 0)
{
return EventQueue.Dequeue();
}
else
{
return new WebSocketEvent()
{
ClientId = 0,
Payload = null,
Type = WebSocketEvent.WebSocketEventType.Nothing,
Error = null,
Reason = null
};
}
}
public void OnOpen()
{
EventQueue.Enqueue(new WebSocketEvent()
{
ClientId = 0,
Payload = null,
Type = WebSocketEvent.WebSocketEventType.Open,
Error = null,
Reason = null
});
}
public void OnMessage(ArraySegment<byte> data)
{
EventQueue.Enqueue(new WebSocketEvent()
{
ClientId = 0,
Payload = data.Array,
Type = WebSocketEvent.WebSocketEventType.Payload,
Error = null,
Reason = null
});
}
public void OnError(string error)
{
EventQueue.Enqueue(new WebSocketEvent()
{
ClientId = 0,
Payload = null,
Type = WebSocketEvent.WebSocketEventType.Error,
Error = error,
Reason = null
});
}
public void OnClose(CloseStatusCode code)
{
EventQueue.Enqueue(new WebSocketEvent()
{
ClientId = 0,
Payload = null,
Type = WebSocketEvent.WebSocketEventType.Close,
Error = null,
Reason = null
});
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52dbfe0eba2c84402aeebb03e0319f78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,126 @@
var LibraryWebSocket = {
$state: {
url: null,
ws: null,
debug: false,
onOpen: null,
onMessage: null,
onError: null,
onClose: null,
},
_SetUrl: function (urlPointer) {
state.url = Pointer_stringify(urlPointer);
},
_SetOnOpen: function (callback) {
state.onOpen = callback;
},
_SetOnMessage: function (callback) {
state.onMessage = callback;
},
_SetOnError: function (callback) {
state.onError = callback;
},
_SetOnClose: function (callback) {
state.onClose = callback;
},
_Connect: function () {
state.ws = new WebSocket(state.url);
state.ws.binaryType = 'arraybuffer';
state.ws.onopen = function () {
if (state.debug) {
console.log("[MLAPI.WebSocket] Connected.");
}
if (state.onOpen) {
Runtime.dynCall('v', state.onOpen, []);
}
};
state.ws.onmessage = function (ev) {
if (state.debug) {
console.log("[MLAPI.WebSocket] Received message:", ev.data);
}
if (!state.onMessage) {
return;
}
if (ev.data instanceof ArrayBuffer) {
var dataBuffer = new Uint8Array(ev.data);
var buffer = _malloc(dataBuffer.length);
HEAPU8.set(dataBuffer, buffer);
try {
Runtime.dynCall('vii', state.onMessage, [buffer, dataBuffer.length]);
} finally {
_free(buffer);
}
}
};
state.ws.onerror = function (ev) {
if (state.debug) {
console.log("[MLAPI.WebSocket] Error occured.");
}
if (state.onError) {
var msg = "WebSocket error.";
var msgBytes = lengthBytesUTF8(msg);
var msgBuffer = _malloc(msgBytes + 1);
stringToUTF8(msg, msgBuffer, msgBytes);
try {
Runtime.dynCall('vi', state.onError, [msgBuffer]);
} finally {
_free(msgBuffer);
}
}
};
state.ws.onclose = function (ev) {
if (state.debug) {
console.log("[MLAPI.WebSocket] Closed.");
}
if (state.onClose) {
Runtime.dynCall('vi', state.onClose, [ev.code]);
}
};
},
_Close: function (code, reasonPointer) {
if (!state.ws) return -3;
if (state.ws.readyState === 2) return -4;
if (state.ws.readyState === 3) return -5;
var reason = (reasonPointer ? Pointer_stringify(reasonPointer) : undefined);
try {
state.ws.close(code, reason);
} catch (err) {
return -7;
}
},
_Send: function (bufferPtr, offset, count) {
if (!state.ws) return -3;
if (state.ws.readyState !== 1) return -6;
state.ws.send(HEAPU8.buffer.slice(bufferPtr + offset, bufferPtr + count - offset));
},
_GetState: function () {
return state.ws ? state.ws.readyState : 3;
}
};
autoAddDeps(LibraryWebSocket, '$state');
mergeInto(LibraryManager.library, LibraryWebSocket);

Просмотреть файл

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: c89ffb278eb4242d8b3fc461706111c9
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
WebGL: WebGL
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 17d281f29ed01435488ba28b87a115da
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f2b621c0e4c824392a268a32d19c8424
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,14 @@
## Ignore build results and temporary files.
Backup*
_UpgradeReport_Files
bin
obj
*.mdb
*.pdb
*.pidb
*.suo
*.user
*.userprefs
UpgradeLog*.*

Просмотреть файл

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2010-2021 sta.blockhead
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a4743919fec5f45189f5184def37b6c2
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,4 @@
# websocket-sharp
Cloned from [github.com/sta/websocket-sharp](https://github.com/sta/websocket-sharp) `master` branch.
This contains the same files as the original repository, however `websocket-sharp/AssemblyInfo.cs` file was removed as we don't need to build the related DLL. Other files like the original `README.md` and examples were also removed.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 50b2f7be61bb9409eb88b32efe47eae9
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 63e3b01c0529049988f12dd7dee508c4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,47 @@
#region License
/*
* ByteOrder.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Specifies the byte order.
/// </summary>
public enum ByteOrder
{
/// <summary>
/// Specifies Little-endian.
/// </summary>
Little,
/// <summary>
/// Specifies Big-endian.
/// </summary>
Big
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a725b78f78c144fc1861afbd7861700f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,113 @@
#region License
/*
* CloseEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012-2019 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Represents the event data for the <see cref="WebSocket.OnClose"/> event.
/// </summary>
/// <remarks>
/// <para>
/// That event occurs when the WebSocket connection has been closed.
/// </para>
/// <para>
/// If you would like to get the reason for the connection close, you should
/// access the <see cref="Code"/> or <see cref="Reason"/> property.
/// </para>
/// </remarks>
public class CloseEventArgs : EventArgs
{
#region Private Fields
private bool _clean;
private PayloadData _payloadData;
#endregion
#region Internal Constructors
internal CloseEventArgs (PayloadData payloadData, bool clean)
{
_payloadData = payloadData;
_clean = clean;
}
internal CloseEventArgs (ushort code, string reason, bool clean)
{
_payloadData = new PayloadData (code, reason);
_clean = clean;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the status code for the connection close.
/// </summary>
/// <value>
/// A <see cref="ushort"/> that represents the status code for
/// the connection close if present.
/// </value>
public ushort Code {
get {
return _payloadData.Code;
}
}
/// <summary>
/// Gets the reason for the connection close.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the reason for
/// the connection close if present.
/// </value>
public string Reason {
get {
return _payloadData.Reason;
}
}
/// <summary>
/// Gets a value indicating whether the connection has been closed cleanly.
/// </summary>
/// <value>
/// <c>true</c> if the connection has been closed cleanly; otherwise,
/// <c>false</c>.
/// </value>
public bool WasClean {
get {
return _clean;
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30d1ff7393a6945ce9ef3b5a9675c253
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,120 @@
#region License
/*
* CloseStatusCode.cs
*
* The MIT License
*
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Indicates the status code for the WebSocket connection close.
/// </summary>
/// <remarks>
/// <para>
/// The values of this enumeration are defined in
/// <see href="http://tools.ietf.org/html/rfc6455#section-7.4">
/// Section 7.4</see> of RFC 6455.
/// </para>
/// <para>
/// "Reserved value" cannot be sent as a status code in
/// closing handshake by an endpoint.
/// </para>
/// </remarks>
public enum CloseStatusCode : ushort
{
/// <summary>
/// Equivalent to close status 1000. Indicates normal close.
/// </summary>
Normal = 1000,
/// <summary>
/// Equivalent to close status 1001. Indicates that an endpoint is
/// going away.
/// </summary>
Away = 1001,
/// <summary>
/// Equivalent to close status 1002. Indicates that an endpoint is
/// terminating the connection due to a protocol error.
/// </summary>
ProtocolError = 1002,
/// <summary>
/// Equivalent to close status 1003. Indicates that an endpoint is
/// terminating the connection because it has received a type of
/// data that it cannot accept.
/// </summary>
UnsupportedData = 1003,
/// <summary>
/// Equivalent to close status 1004. Still undefined. A Reserved value.
/// </summary>
Undefined = 1004,
/// <summary>
/// Equivalent to close status 1005. Indicates that no status code was
/// actually present. A Reserved value.
/// </summary>
NoStatus = 1005,
/// <summary>
/// Equivalent to close status 1006. Indicates that the connection was
/// closed abnormally. A Reserved value.
/// </summary>
Abnormal = 1006,
/// <summary>
/// Equivalent to close status 1007. Indicates that an endpoint is
/// terminating the connection because it has received a message that
/// contains data that is not consistent with the type of the message.
/// </summary>
InvalidData = 1007,
/// <summary>
/// Equivalent to close status 1008. Indicates that an endpoint is
/// terminating the connection because it has received a message that
/// violates its policy.
/// </summary>
PolicyViolation = 1008,
/// <summary>
/// Equivalent to close status 1009. Indicates that an endpoint is
/// terminating the connection because it has received a message that
/// is too big to process.
/// </summary>
TooBig = 1009,
/// <summary>
/// Equivalent to close status 1010. Indicates that a client is
/// terminating the connection because it has expected the server to
/// negotiate one or more extension, but the server did not return
/// them in the handshake response.
/// </summary>
MandatoryExtension = 1010,
/// <summary>
/// Equivalent to close status 1011. Indicates that a server is
/// terminating the connection because it has encountered an unexpected
/// condition that prevented it from fulfilling the request.
/// </summary>
ServerError = 1011,
/// <summary>
/// Equivalent to close status 1015. Indicates that the connection was
/// closed due to a failure to perform a TLS handshake. A Reserved value.
/// </summary>
TlsHandshakeFailure = 1015
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9267b5a40b24f4160941a8887b531cff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,52 @@
#region License
/*
* CompressionMethod.cs
*
* The MIT License
*
* Copyright (c) 2013-2017 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Specifies the method for compression.
/// </summary>
/// <remarks>
/// The methods are defined in
/// <see href="https://tools.ietf.org/html/rfc7692">
/// Compression Extensions for WebSocket</see>.
/// </remarks>
public enum CompressionMethod : byte
{
/// <summary>
/// Specifies no compression.
/// </summary>
None,
/// <summary>
/// Specifies DEFLATE.
/// </summary>
Deflate
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1a224d1c619343a1b2fd02d6b4bbbec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,109 @@
#region License
/*
* ErrorEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Contributors
/*
* Contributors:
* - Frank Razenberg <frank@zzattack.org>
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Represents the event data for the <see cref="WebSocket.OnError"/> event.
/// </summary>
/// <remarks>
/// <para>
/// That event occurs when the <see cref="WebSocket"/> gets an error.
/// </para>
/// <para>
/// If you would like to get the error message, you should access
/// the <see cref="ErrorEventArgs.Message"/> property.
/// </para>
/// <para>
/// And if the error is due to an exception, you can get it by accessing
/// the <see cref="ErrorEventArgs.Exception"/> property.
/// </para>
/// </remarks>
public class ErrorEventArgs : EventArgs
{
#region Private Fields
private Exception _exception;
private string _message;
#endregion
#region Internal Constructors
internal ErrorEventArgs (string message)
: this (message, null)
{
}
internal ErrorEventArgs (string message, Exception exception)
{
_message = message;
_exception = exception;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the exception that caused the error.
/// </summary>
/// <value>
/// An <see cref="System.Exception"/> instance that represents the cause of
/// the error if it is due to an exception; otherwise, <see langword="null"/>.
/// </value>
public Exception Exception {
get {
return _exception;
}
}
/// <summary>
/// Gets the error message.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the error message.
/// </value>
public string Message {
get {
return _message;
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56cf4175497f344b5854ab217026da22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80614531c2687444d85d87648f4e0e7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,51 @@
#region License
/*
* Fin.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Indicates whether a WebSocket frame is the final frame of a message.
/// </summary>
/// <remarks>
/// The values of this enumeration are defined in
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
/// </remarks>
internal enum Fin : byte
{
/// <summary>
/// Equivalent to numeric value 0. Indicates more frames of a message follow.
/// </summary>
More = 0x0,
/// <summary>
/// Equivalent to numeric value 1. Indicates the final frame of a message.
/// </summary>
Final = 0x1
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d5494ffde6508404a97a25ff82b6865e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,208 @@
#region License
/*
* HttpBase.cs
*
* The MIT License
*
* Copyright (c) 2012-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Threading;
using WebSocketSharp.Net;
namespace WebSocketSharp
{
internal abstract class HttpBase
{
#region Private Fields
private NameValueCollection _headers;
private const int _headersMaxLength = 8192;
private Version _version;
#endregion
#region Internal Fields
internal byte[] EntityBodyData;
#endregion
#region Protected Fields
protected const string CrLf = "\r\n";
#endregion
#region Protected Constructors
protected HttpBase (Version version, NameValueCollection headers)
{
_version = version;
_headers = headers;
}
#endregion
#region Public Properties
public string EntityBody {
get {
if (EntityBodyData == null || EntityBodyData.LongLength == 0)
return String.Empty;
Encoding enc = null;
var contentType = _headers["Content-Type"];
if (contentType != null && contentType.Length > 0)
enc = HttpUtility.GetEncoding (contentType);
return (enc ?? Encoding.UTF8).GetString (EntityBodyData);
}
}
public NameValueCollection Headers {
get {
return _headers;
}
}
public Version ProtocolVersion {
get {
return _version;
}
}
#endregion
#region Private Methods
private static byte[] readEntityBody (Stream stream, string length)
{
long len;
if (!Int64.TryParse (length, out len))
throw new ArgumentException ("Cannot be parsed.", "length");
if (len < 0)
throw new ArgumentOutOfRangeException ("length", "Less than zero.");
return len > 1024
? stream.ReadBytes (len, 1024)
: len > 0
? stream.ReadBytes ((int) len)
: null;
}
private static string[] readHeaders (Stream stream, int maxLength)
{
var buff = new List<byte> ();
var cnt = 0;
Action<int> add = i => {
if (i == -1)
throw new EndOfStreamException ("The header cannot be read from the data source.");
buff.Add ((byte) i);
cnt++;
};
var read = false;
while (cnt < maxLength) {
if (stream.ReadByte ().EqualsWith ('\r', add) &&
stream.ReadByte ().EqualsWith ('\n', add) &&
stream.ReadByte ().EqualsWith ('\r', add) &&
stream.ReadByte ().EqualsWith ('\n', add)) {
read = true;
break;
}
}
if (!read)
throw new WebSocketException ("The length of header part is greater than the max length.");
return Encoding.UTF8.GetString (buff.ToArray ())
.Replace (CrLf + " ", " ")
.Replace (CrLf + "\t", " ")
.Split (new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries);
}
#endregion
#region Protected Methods
protected static T Read<T> (Stream stream, Func<string[], T> parser, int millisecondsTimeout)
where T : HttpBase
{
var timeout = false;
var timer = new Timer (
state => {
timeout = true;
stream.Close ();
},
null,
millisecondsTimeout,
-1);
T http = null;
Exception exception = null;
try {
http = parser (readHeaders (stream, _headersMaxLength));
var contentLen = http.Headers["Content-Length"];
if (contentLen != null && contentLen.Length > 0)
http.EntityBodyData = readEntityBody (stream, contentLen);
}
catch (Exception ex) {
exception = ex;
}
finally {
timer.Change (-1, -1);
timer.Dispose ();
}
var msg = timeout
? "A timeout has occurred while reading an HTTP request/response."
: exception != null
? "An exception has occurred while reading an HTTP request/response."
: null;
if (msg != null)
throw new WebSocketException (msg, exception);
return http;
}
#endregion
#region Public Methods
public byte[] ToByteArray ()
{
return Encoding.UTF8.GetBytes (ToString ());
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 799975266be9b4b4c88362bcb7ff9137
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,217 @@
#region License
/*
* HttpRequest.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Contributors
/*
* Contributors:
* - David Burhans
*/
#endregion
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using WebSocketSharp.Net;
namespace WebSocketSharp
{
internal class HttpRequest : HttpBase
{
#region Private Fields
private CookieCollection _cookies;
private string _method;
private string _uri;
#endregion
#region Private Constructors
private HttpRequest (string method, string uri, Version version, NameValueCollection headers)
: base (version, headers)
{
_method = method;
_uri = uri;
}
#endregion
#region Internal Constructors
internal HttpRequest (string method, string uri)
: this (method, uri, HttpVersion.Version11, new NameValueCollection ())
{
Headers["User-Agent"] = "websocket-sharp/1.0";
}
#endregion
#region Public Properties
public AuthenticationResponse AuthenticationResponse {
get {
var res = Headers["Authorization"];
return res != null && res.Length > 0
? AuthenticationResponse.Parse (res)
: null;
}
}
public CookieCollection Cookies {
get {
if (_cookies == null)
_cookies = Headers.GetCookies (false);
return _cookies;
}
}
public string HttpMethod {
get {
return _method;
}
}
public bool IsWebSocketRequest {
get {
return _method == "GET"
&& ProtocolVersion > HttpVersion.Version10
&& Headers.Upgrades ("websocket");
}
}
public string RequestUri {
get {
return _uri;
}
}
#endregion
#region Internal Methods
internal static HttpRequest CreateConnectRequest (Uri uri)
{
var host = uri.DnsSafeHost;
var port = uri.Port;
var authority = String.Format ("{0}:{1}", host, port);
var req = new HttpRequest ("CONNECT", authority);
req.Headers["Host"] = port == 80 ? host : authority;
return req;
}
internal static HttpRequest CreateWebSocketRequest (Uri uri)
{
var req = new HttpRequest ("GET", uri.PathAndQuery);
var headers = req.Headers;
// Only includes a port number in the Host header value if it's non-default.
// See: https://tools.ietf.org/html/rfc6455#page-17
var port = uri.Port;
var schm = uri.Scheme;
headers["Host"] = (port == 80 && schm == "ws") || (port == 443 && schm == "wss")
? uri.DnsSafeHost
: uri.Authority;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
return req;
}
internal HttpResponse GetResponse (Stream stream, int millisecondsTimeout)
{
var buff = ToByteArray ();
stream.Write (buff, 0, buff.Length);
return Read<HttpResponse> (stream, HttpResponse.Parse, millisecondsTimeout);
}
internal static HttpRequest Parse (string[] headerParts)
{
var requestLine = headerParts[0].Split (new[] { ' ' }, 3);
if (requestLine.Length != 3)
throw new ArgumentException ("Invalid request line: " + headerParts[0]);
var headers = new WebHeaderCollection ();
for (int i = 1; i < headerParts.Length; i++)
headers.InternalSet (headerParts[i], false);
return new HttpRequest (
requestLine[0], requestLine[1], new Version (requestLine[2].Substring (5)), headers);
}
internal static HttpRequest Read (Stream stream, int millisecondsTimeout)
{
return Read<HttpRequest> (stream, Parse, millisecondsTimeout);
}
#endregion
#region Public Methods
public void SetCookies (CookieCollection cookies)
{
if (cookies == null || cookies.Count == 0)
return;
var buff = new StringBuilder (64);
foreach (var cookie in cookies.Sorted)
if (!cookie.Expired)
buff.AppendFormat ("{0}; ", cookie.ToString ());
var len = buff.Length;
if (len > 2) {
buff.Length = len - 2;
Headers["Cookie"] = buff.ToString ();
}
}
public override string ToString ()
{
var output = new StringBuilder (64);
output.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf);
var headers = Headers;
foreach (var key in headers.AllKeys)
output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
output.Append (CrLf);
var entity = EntityBody;
if (entity.Length > 0)
output.Append (entity);
return output.ToString ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 437dc178142cc4967aa1cd13c31481bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,209 @@
#region License
/*
* HttpResponse.cs
*
* The MIT License
*
* Copyright (c) 2012-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using WebSocketSharp.Net;
namespace WebSocketSharp
{
internal class HttpResponse : HttpBase
{
#region Private Fields
private string _code;
private string _reason;
#endregion
#region Private Constructors
private HttpResponse (string code, string reason, Version version, NameValueCollection headers)
: base (version, headers)
{
_code = code;
_reason = reason;
}
#endregion
#region Internal Constructors
internal HttpResponse (HttpStatusCode code)
: this (code, code.GetDescription ())
{
}
internal HttpResponse (HttpStatusCode code, string reason)
: this (((int) code).ToString (), reason, HttpVersion.Version11, new NameValueCollection ())
{
Headers["Server"] = "websocket-sharp/1.0";
}
#endregion
#region Public Properties
public CookieCollection Cookies {
get {
return Headers.GetCookies (true);
}
}
public bool HasConnectionClose {
get {
var comparison = StringComparison.OrdinalIgnoreCase;
return Headers.Contains ("Connection", "close", comparison);
}
}
public bool IsProxyAuthenticationRequired {
get {
return _code == "407";
}
}
public bool IsRedirect {
get {
return _code == "301" || _code == "302";
}
}
public bool IsUnauthorized {
get {
return _code == "401";
}
}
public bool IsWebSocketResponse {
get {
return ProtocolVersion > HttpVersion.Version10
&& _code == "101"
&& Headers.Upgrades ("websocket");
}
}
public string Reason {
get {
return _reason;
}
}
public string StatusCode {
get {
return _code;
}
}
#endregion
#region Internal Methods
internal static HttpResponse CreateCloseResponse (HttpStatusCode code)
{
var res = new HttpResponse (code);
res.Headers["Connection"] = "close";
return res;
}
internal static HttpResponse CreateUnauthorizedResponse (string challenge)
{
var res = new HttpResponse (HttpStatusCode.Unauthorized);
res.Headers["WWW-Authenticate"] = challenge;
return res;
}
internal static HttpResponse CreateWebSocketResponse ()
{
var res = new HttpResponse (HttpStatusCode.SwitchingProtocols);
var headers = res.Headers;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
return res;
}
internal static HttpResponse Parse (string[] headerParts)
{
var statusLine = headerParts[0].Split (new[] { ' ' }, 3);
if (statusLine.Length != 3)
throw new ArgumentException ("Invalid status line: " + headerParts[0]);
var headers = new WebHeaderCollection ();
for (int i = 1; i < headerParts.Length; i++)
headers.InternalSet (headerParts[i], true);
return new HttpResponse (
statusLine[1], statusLine[2], new Version (statusLine[0].Substring (5)), headers);
}
internal static HttpResponse Read (Stream stream, int millisecondsTimeout)
{
return Read<HttpResponse> (stream, Parse, millisecondsTimeout);
}
#endregion
#region Public Methods
public void SetCookies (CookieCollection cookies)
{
if (cookies == null || cookies.Count == 0)
return;
var headers = Headers;
foreach (var cookie in cookies.Sorted)
headers.Add ("Set-Cookie", cookie.ToResponseString ());
}
public override string ToString ()
{
var output = new StringBuilder (64);
output.AppendFormat ("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf);
var headers = Headers;
foreach (var key in headers.AllKeys)
output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
output.Append (CrLf);
var entity = EntityBody;
if (entity.Length > 0)
output.Append (entity);
return output.ToString ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fc5ce5d82c3c40618fc7dcde8b724f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,149 @@
#region License
/*
* LogData.cs
*
* The MIT License
*
* Copyright (c) 2013-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Diagnostics;
using System.Text;
namespace WebSocketSharp
{
/// <summary>
/// Represents a log data used by the <see cref="Logger"/> class.
/// </summary>
public class LogData
{
#region Private Fields
private StackFrame _caller;
private DateTime _date;
private LogLevel _level;
private string _message;
#endregion
#region Internal Constructors
internal LogData (LogLevel level, StackFrame caller, string message)
{
_level = level;
_caller = caller;
_message = message ?? String.Empty;
_date = DateTime.Now;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the information of the logging method caller.
/// </summary>
/// <value>
/// A <see cref="StackFrame"/> that provides the information of the logging method caller.
/// </value>
public StackFrame Caller {
get {
return _caller;
}
}
/// <summary>
/// Gets the date and time when the log data was created.
/// </summary>
/// <value>
/// A <see cref="DateTime"/> that represents the date and time when the log data was created.
/// </value>
public DateTime Date {
get {
return _date;
}
}
/// <summary>
/// Gets the logging level of the log data.
/// </summary>
/// <value>
/// One of the <see cref="LogLevel"/> enum values, indicates the logging level of the log data.
/// </value>
public LogLevel Level {
get {
return _level;
}
}
/// <summary>
/// Gets the message of the log data.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the message of the log data.
/// </value>
public string Message {
get {
return _message;
}
}
#endregion
#region Public Methods
/// <summary>
/// Returns a <see cref="string"/> that represents the current <see cref="LogData"/>.
/// </summary>
/// <returns>
/// A <see cref="string"/> that represents the current <see cref="LogData"/>.
/// </returns>
public override string ToString ()
{
var header = String.Format ("{0}|{1,-5}|", _date, _level);
var method = _caller.GetMethod ();
var type = method.DeclaringType;
#if DEBUG
var lineNum = _caller.GetFileLineNumber ();
var headerAndCaller =
String.Format ("{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum);
#else
var headerAndCaller = String.Format ("{0}{1}.{2}|", header, type.Name, method.Name);
#endif
var msgs = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n');
if (msgs.Length <= 1)
return String.Format ("{0}{1}", headerAndCaller, _message);
var buff = new StringBuilder (String.Format ("{0}{1}\n", headerAndCaller, msgs[0]), 64);
var fmt = String.Format ("{{0,{0}}}{{1}}\n", header.Length);
for (var i = 1; i < msgs.Length; i++)
buff.AppendFormat (fmt, "", msgs[i]);
buff.Length--;
return buff.ToString ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2340ae50e51984adeaac2cbe83807723
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,63 @@
#region License
/*
* LogLevel.cs
*
* The MIT License
*
* Copyright (c) 2013-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Specifies the logging level.
/// </summary>
public enum LogLevel
{
/// <summary>
/// Specifies the bottom logging level.
/// </summary>
Trace,
/// <summary>
/// Specifies the 2nd logging level from the bottom.
/// </summary>
Debug,
/// <summary>
/// Specifies the 3rd logging level from the bottom.
/// </summary>
Info,
/// <summary>
/// Specifies the 3rd logging level from the top.
/// </summary>
Warn,
/// <summary>
/// Specifies the 2nd logging level from the top.
/// </summary>
Error,
/// <summary>
/// Specifies the top logging level.
/// </summary>
Fatal
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22aa721c595044dca8e23da4c56f2550
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,330 @@
#region License
/*
* Logger.cs
*
* The MIT License
*
* Copyright (c) 2013-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Diagnostics;
using System.IO;
namespace WebSocketSharp
{
/// <summary>
/// Provides a set of methods and properties for logging.
/// </summary>
/// <remarks>
/// <para>
/// If you output a log with lower than the value of the <see cref="Logger.Level"/> property,
/// it cannot be outputted.
/// </para>
/// <para>
/// The default output action writes a log to the standard output stream and the log file
/// if the <see cref="Logger.File"/> property has a valid path to it.
/// </para>
/// <para>
/// If you would like to use the custom output action, you should set
/// the <see cref="Logger.Output"/> property to any <c>Action&lt;LogData, string&gt;</c>
/// delegate.
/// </para>
/// </remarks>
public class Logger
{
#region Private Fields
private volatile string _file;
private volatile LogLevel _level;
private Action<LogData, string> _output;
private object _sync;
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class.
/// </summary>
/// <remarks>
/// This constructor initializes the current logging level with <see cref="LogLevel.Error"/>.
/// </remarks>
public Logger ()
: this (LogLevel.Error, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class with
/// the specified logging <paramref name="level"/>.
/// </summary>
/// <param name="level">
/// One of the <see cref="LogLevel"/> enum values.
/// </param>
public Logger (LogLevel level)
: this (level, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class with
/// the specified logging <paramref name="level"/>, path to the log <paramref name="file"/>,
/// and <paramref name="output"/> action.
/// </summary>
/// <param name="level">
/// One of the <see cref="LogLevel"/> enum values.
/// </param>
/// <param name="file">
/// A <see cref="string"/> that represents the path to the log file.
/// </param>
/// <param name="output">
/// An <c>Action&lt;LogData, string&gt;</c> delegate that references the method(s) used to
/// output a log. A <see cref="string"/> parameter passed to this delegate is
/// <paramref name="file"/>.
/// </param>
public Logger (LogLevel level, string file, Action<LogData, string> output)
{
_level = level;
_file = file;
_output = output ?? defaultOutput;
_sync = new object ();
}
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the current path to the log file.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the current path to the log file if any.
/// </value>
public string File {
get {
return _file;
}
set {
lock (_sync) {
_file = value;
Warn (
String.Format ("The current path to the log file has been changed to {0}.", _file));
}
}
}
/// <summary>
/// Gets or sets the current logging level.
/// </summary>
/// <remarks>
/// A log with lower than the value of this property cannot be outputted.
/// </remarks>
/// <value>
/// One of the <see cref="LogLevel"/> enum values, specifies the current logging level.
/// </value>
public LogLevel Level {
get {
return _level;
}
set {
lock (_sync) {
_level = value;
Warn (String.Format ("The current logging level has been changed to {0}.", _level));
}
}
}
/// <summary>
/// Gets or sets the current output action used to output a log.
/// </summary>
/// <value>
/// <para>
/// An <c>Action&lt;LogData, string&gt;</c> delegate that references the method(s) used to
/// output a log. A <see cref="string"/> parameter passed to this delegate is the value of
/// the <see cref="Logger.File"/> property.
/// </para>
/// <para>
/// If the value to set is <see langword="null"/>, the current output action is changed to
/// the default output action.
/// </para>
/// </value>
public Action<LogData, string> Output {
get {
return _output;
}
set {
lock (_sync) {
_output = value ?? defaultOutput;
Warn ("The current output action has been changed.");
}
}
}
#endregion
#region Private Methods
private static void defaultOutput (LogData data, string path)
{
var log = data.ToString ();
Console.WriteLine (log);
if (path != null && path.Length > 0)
writeToFile (log, path);
}
private void output (string message, LogLevel level)
{
lock (_sync) {
if (_level > level)
return;
LogData data = null;
try {
data = new LogData (level, new StackFrame (2, true), message);
_output (data, _file);
}
catch (Exception ex) {
data = new LogData (LogLevel.Fatal, new StackFrame (0, true), ex.Message);
Console.WriteLine (data.ToString ());
}
}
}
private static void writeToFile (string value, string path)
{
using (var writer = new StreamWriter (path, true))
using (var syncWriter = TextWriter.Synchronized (writer))
syncWriter.WriteLine (value);
}
#endregion
#region Public Methods
/// <summary>
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Debug"/>.
/// </summary>
/// <remarks>
/// If the current logging level is higher than <see cref="LogLevel.Debug"/>,
/// this method doesn't output <paramref name="message"/> as a log.
/// </remarks>
/// <param name="message">
/// A <see cref="string"/> that represents the message to output as a log.
/// </param>
public void Debug (string message)
{
if (_level > LogLevel.Debug)
return;
output (message, LogLevel.Debug);
}
/// <summary>
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Error"/>.
/// </summary>
/// <remarks>
/// If the current logging level is higher than <see cref="LogLevel.Error"/>,
/// this method doesn't output <paramref name="message"/> as a log.
/// </remarks>
/// <param name="message">
/// A <see cref="string"/> that represents the message to output as a log.
/// </param>
public void Error (string message)
{
if (_level > LogLevel.Error)
return;
output (message, LogLevel.Error);
}
/// <summary>
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Fatal"/>.
/// </summary>
/// <param name="message">
/// A <see cref="string"/> that represents the message to output as a log.
/// </param>
public void Fatal (string message)
{
output (message, LogLevel.Fatal);
}
/// <summary>
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Info"/>.
/// </summary>
/// <remarks>
/// If the current logging level is higher than <see cref="LogLevel.Info"/>,
/// this method doesn't output <paramref name="message"/> as a log.
/// </remarks>
/// <param name="message">
/// A <see cref="string"/> that represents the message to output as a log.
/// </param>
public void Info (string message)
{
if (_level > LogLevel.Info)
return;
output (message, LogLevel.Info);
}
/// <summary>
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Trace"/>.
/// </summary>
/// <remarks>
/// If the current logging level is higher than <see cref="LogLevel.Trace"/>,
/// this method doesn't output <paramref name="message"/> as a log.
/// </remarks>
/// <param name="message">
/// A <see cref="string"/> that represents the message to output as a log.
/// </param>
public void Trace (string message)
{
if (_level > LogLevel.Trace)
return;
output (message, LogLevel.Trace);
}
/// <summary>
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Warn"/>.
/// </summary>
/// <remarks>
/// If the current logging level is higher than <see cref="LogLevel.Warn"/>,
/// this method doesn't output <paramref name="message"/> as a log.
/// </remarks>
/// <param name="message">
/// A <see cref="string"/> that represents the message to output as a log.
/// </param>
public void Warn (string message)
{
if (_level > LogLevel.Warn)
return;
output (message, LogLevel.Warn);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b87db541afe44ff2bd7a7abf5496091
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,51 @@
#region License
/*
* Mask.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Indicates whether the payload data of a WebSocket frame is masked.
/// </summary>
/// <remarks>
/// The values of this enumeration are defined in
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
/// </remarks>
internal enum Mask : byte
{
/// <summary>
/// Equivalent to numeric value 0. Indicates not masked.
/// </summary>
Off = 0x0,
/// <summary>
/// Equivalent to numeric value 1. Indicates masked.
/// </summary>
On = 0x1
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b80c9a2981b141a9a12ee7d4629801c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,183 @@
#region License
/*
* MessageEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Represents the event data for the <see cref="WebSocket.OnMessage"/> event.
/// </summary>
/// <remarks>
/// <para>
/// That event occurs when the <see cref="WebSocket"/> receives
/// a message or a ping if the <see cref="WebSocket.EmitOnPing"/>
/// property is set to <c>true</c>.
/// </para>
/// <para>
/// If you would like to get the message data, you should access
/// the <see cref="Data"/> or <see cref="RawData"/> property.
/// </para>
/// </remarks>
public class MessageEventArgs : EventArgs
{
#region Private Fields
private string _data;
private bool _dataSet;
private Opcode _opcode;
private byte[] _rawData;
#endregion
#region Internal Constructors
internal MessageEventArgs (WebSocketFrame frame)
{
_opcode = frame.Opcode;
_rawData = frame.PayloadData.ApplicationData;
}
internal MessageEventArgs (Opcode opcode, byte[] rawData)
{
if ((ulong) rawData.LongLength > PayloadData.MaxLength)
throw new WebSocketException (CloseStatusCode.TooBig);
_opcode = opcode;
_rawData = rawData;
}
#endregion
#region Internal Properties
/// <summary>
/// Gets the opcode for the message.
/// </summary>
/// <value>
/// <see cref="Opcode.Text"/>, <see cref="Opcode.Binary"/>,
/// or <see cref="Opcode.Ping"/>.
/// </value>
internal Opcode Opcode {
get {
return _opcode;
}
}
#endregion
#region Public Properties
/// <summary>
/// Gets the message data as a <see cref="string"/>.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the message data if its type is
/// text or ping and if decoding it to a string has successfully done;
/// otherwise, <see langword="null"/>.
/// </value>
public string Data {
get {
setData ();
return _data;
}
}
/// <summary>
/// Gets a value indicating whether the message type is binary.
/// </summary>
/// <value>
/// <c>true</c> if the message type is binary; otherwise, <c>false</c>.
/// </value>
public bool IsBinary {
get {
return _opcode == Opcode.Binary;
}
}
/// <summary>
/// Gets a value indicating whether the message type is ping.
/// </summary>
/// <value>
/// <c>true</c> if the message type is ping; otherwise, <c>false</c>.
/// </value>
public bool IsPing {
get {
return _opcode == Opcode.Ping;
}
}
/// <summary>
/// Gets a value indicating whether the message type is text.
/// </summary>
/// <value>
/// <c>true</c> if the message type is text; otherwise, <c>false</c>.
/// </value>
public bool IsText {
get {
return _opcode == Opcode.Text;
}
}
/// <summary>
/// Gets the message data as an array of <see cref="byte"/>.
/// </summary>
/// <value>
/// An array of <see cref="byte"/> that represents the message data.
/// </value>
public byte[] RawData {
get {
setData ();
return _rawData;
}
}
#endregion
#region Private Methods
private void setData ()
{
if (_dataSet)
return;
if (_opcode == Opcode.Binary) {
_dataSet = true;
return;
}
string data;
if (_rawData.TryGetUTF8DecodedString (out data))
_data = data;
_dataSet = true;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4730577dd56540e2b7fe4f373756613
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: af0efd83b780d4e26ad9f604fb354c5d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,151 @@
#region License
/*
* AuthenticationBase.cs
*
* The MIT License
*
* Copyright (c) 2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Specialized;
using System.Text;
namespace WebSocketSharp.Net
{
internal abstract class AuthenticationBase
{
#region Private Fields
private AuthenticationSchemes _scheme;
#endregion
#region Internal Fields
internal NameValueCollection Parameters;
#endregion
#region Protected Constructors
protected AuthenticationBase (AuthenticationSchemes scheme, NameValueCollection parameters)
{
_scheme = scheme;
Parameters = parameters;
}
#endregion
#region Public Properties
public string Algorithm {
get {
return Parameters["algorithm"];
}
}
public string Nonce {
get {
return Parameters["nonce"];
}
}
public string Opaque {
get {
return Parameters["opaque"];
}
}
public string Qop {
get {
return Parameters["qop"];
}
}
public string Realm {
get {
return Parameters["realm"];
}
}
public AuthenticationSchemes Scheme {
get {
return _scheme;
}
}
#endregion
#region Internal Methods
internal static string CreateNonceValue ()
{
var src = new byte[16];
var rand = new Random ();
rand.NextBytes (src);
var res = new StringBuilder (32);
foreach (var b in src)
res.Append (b.ToString ("x2"));
return res.ToString ();
}
internal static NameValueCollection ParseParameters (string value)
{
var res = new NameValueCollection ();
foreach (var param in value.SplitHeaderValue (',')) {
var i = param.IndexOf ('=');
var name = i > 0 ? param.Substring (0, i).Trim () : null;
var val = i < 0
? param.Trim ().Trim ('"')
: i < param.Length - 1
? param.Substring (i + 1).Trim ().Trim ('"')
: String.Empty;
res.Add (name, val);
}
return res;
}
internal abstract string ToBasicString ();
internal abstract string ToDigestString ();
#endregion
#region Public Methods
public override string ToString ()
{
return _scheme == AuthenticationSchemes.Basic
? ToBasicString ()
: _scheme == AuthenticationSchemes.Digest
? ToDigestString ()
: String.Empty;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 964b08f2d05fa4b63adaf18d3860c7ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,146 @@
#region License
/*
* AuthenticationChallenge.cs
*
* The MIT License
*
* Copyright (c) 2013-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Specialized;
using System.Text;
namespace WebSocketSharp.Net
{
internal class AuthenticationChallenge : AuthenticationBase
{
#region Private Constructors
private AuthenticationChallenge (AuthenticationSchemes scheme, NameValueCollection parameters)
: base (scheme, parameters)
{
}
#endregion
#region Internal Constructors
internal AuthenticationChallenge (AuthenticationSchemes scheme, string realm)
: base (scheme, new NameValueCollection ())
{
Parameters["realm"] = realm;
if (scheme == AuthenticationSchemes.Digest) {
Parameters["nonce"] = CreateNonceValue ();
Parameters["algorithm"] = "MD5";
Parameters["qop"] = "auth";
}
}
#endregion
#region Public Properties
public string Domain {
get {
return Parameters["domain"];
}
}
public string Stale {
get {
return Parameters["stale"];
}
}
#endregion
#region Internal Methods
internal static AuthenticationChallenge CreateBasicChallenge (string realm)
{
return new AuthenticationChallenge (AuthenticationSchemes.Basic, realm);
}
internal static AuthenticationChallenge CreateDigestChallenge (string realm)
{
return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm);
}
internal static AuthenticationChallenge Parse (string value)
{
var chal = value.Split (new[] { ' ' }, 2);
if (chal.Length != 2)
return null;
var schm = chal[0].ToLower ();
return schm == "basic"
? new AuthenticationChallenge (
AuthenticationSchemes.Basic, ParseParameters (chal[1]))
: schm == "digest"
? new AuthenticationChallenge (
AuthenticationSchemes.Digest, ParseParameters (chal[1]))
: null;
}
internal override string ToBasicString ()
{
return String.Format ("Basic realm=\"{0}\"", Parameters["realm"]);
}
internal override string ToDigestString ()
{
var output = new StringBuilder (128);
var domain = Parameters["domain"];
if (domain != null)
output.AppendFormat (
"Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"",
Parameters["realm"],
domain,
Parameters["nonce"]);
else
output.AppendFormat (
"Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]);
var opaque = Parameters["opaque"];
if (opaque != null)
output.AppendFormat (", opaque=\"{0}\"", opaque);
var stale = Parameters["stale"];
if (stale != null)
output.AppendFormat (", stale={0}", stale);
var algo = Parameters["algorithm"];
if (algo != null)
output.AppendFormat (", algorithm={0}", algo);
var qop = Parameters["qop"];
if (qop != null)
output.AppendFormat (", qop=\"{0}\"", qop);
return output.ToString ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 830bad6802cf1499584a7cfdb3ef2a28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,323 @@
#region License
/*
* AuthenticationResponse.cs
*
* ParseBasicCredentials is derived from System.Net.HttpListenerContext.cs of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2013-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Specialized;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
namespace WebSocketSharp.Net
{
internal class AuthenticationResponse : AuthenticationBase
{
#region Private Fields
private uint _nonceCount;
#endregion
#region Private Constructors
private AuthenticationResponse (AuthenticationSchemes scheme, NameValueCollection parameters)
: base (scheme, parameters)
{
}
#endregion
#region Internal Constructors
internal AuthenticationResponse (NetworkCredential credentials)
: this (AuthenticationSchemes.Basic, new NameValueCollection (), credentials, 0)
{
}
internal AuthenticationResponse (
AuthenticationChallenge challenge, NetworkCredential credentials, uint nonceCount)
: this (challenge.Scheme, challenge.Parameters, credentials, nonceCount)
{
}
internal AuthenticationResponse (
AuthenticationSchemes scheme,
NameValueCollection parameters,
NetworkCredential credentials,
uint nonceCount)
: base (scheme, parameters)
{
Parameters["username"] = credentials.Username;
Parameters["password"] = credentials.Password;
Parameters["uri"] = credentials.Domain;
_nonceCount = nonceCount;
if (scheme == AuthenticationSchemes.Digest)
initAsDigest ();
}
#endregion
#region Internal Properties
internal uint NonceCount {
get {
return _nonceCount < UInt32.MaxValue
? _nonceCount
: 0;
}
}
#endregion
#region Public Properties
public string Cnonce {
get {
return Parameters["cnonce"];
}
}
public string Nc {
get {
return Parameters["nc"];
}
}
public string Password {
get {
return Parameters["password"];
}
}
public string Response {
get {
return Parameters["response"];
}
}
public string Uri {
get {
return Parameters["uri"];
}
}
public string UserName {
get {
return Parameters["username"];
}
}
#endregion
#region Private Methods
private static string createA1 (string username, string password, string realm)
{
return String.Format ("{0}:{1}:{2}", username, realm, password);
}
private static string createA1 (
string username, string password, string realm, string nonce, string cnonce)
{
return String.Format (
"{0}:{1}:{2}", hash (createA1 (username, password, realm)), nonce, cnonce);
}
private static string createA2 (string method, string uri)
{
return String.Format ("{0}:{1}", method, uri);
}
private static string createA2 (string method, string uri, string entity)
{
return String.Format ("{0}:{1}:{2}", method, uri, hash (entity));
}
private static string hash (string value)
{
var src = Encoding.UTF8.GetBytes (value);
var md5 = MD5.Create ();
var hashed = md5.ComputeHash (src);
var res = new StringBuilder (64);
foreach (var b in hashed)
res.Append (b.ToString ("x2"));
return res.ToString ();
}
private void initAsDigest ()
{
var qops = Parameters["qop"];
if (qops != null) {
if (qops.Split (',').Contains (qop => qop.Trim ().ToLower () == "auth")) {
Parameters["qop"] = "auth";
Parameters["cnonce"] = CreateNonceValue ();
Parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount);
}
else {
Parameters["qop"] = null;
}
}
Parameters["method"] = "GET";
Parameters["response"] = CreateRequestDigest (Parameters);
}
#endregion
#region Internal Methods
internal static string CreateRequestDigest (NameValueCollection parameters)
{
var user = parameters["username"];
var pass = parameters["password"];
var realm = parameters["realm"];
var nonce = parameters["nonce"];
var uri = parameters["uri"];
var algo = parameters["algorithm"];
var qop = parameters["qop"];
var cnonce = parameters["cnonce"];
var nc = parameters["nc"];
var method = parameters["method"];
var a1 = algo != null && algo.ToLower () == "md5-sess"
? createA1 (user, pass, realm, nonce, cnonce)
: createA1 (user, pass, realm);
var a2 = qop != null && qop.ToLower () == "auth-int"
? createA2 (method, uri, parameters["entity"])
: createA2 (method, uri);
var secret = hash (a1);
var data = qop != null
? String.Format ("{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash (a2))
: String.Format ("{0}:{1}", nonce, hash (a2));
return hash (String.Format ("{0}:{1}", secret, data));
}
internal static AuthenticationResponse Parse (string value)
{
try {
var cred = value.Split (new[] { ' ' }, 2);
if (cred.Length != 2)
return null;
var schm = cred[0].ToLower ();
return schm == "basic"
? new AuthenticationResponse (
AuthenticationSchemes.Basic, ParseBasicCredentials (cred[1]))
: schm == "digest"
? new AuthenticationResponse (
AuthenticationSchemes.Digest, ParseParameters (cred[1]))
: null;
}
catch {
}
return null;
}
internal static NameValueCollection ParseBasicCredentials (string value)
{
// Decode the basic-credentials (a Base64 encoded string).
var userPass = Encoding.Default.GetString (Convert.FromBase64String (value));
// The format is [<domain>\]<username>:<password>.
var i = userPass.IndexOf (':');
var user = userPass.Substring (0, i);
var pass = i < userPass.Length - 1 ? userPass.Substring (i + 1) : String.Empty;
// Check if 'domain' exists.
i = user.IndexOf ('\\');
if (i > -1)
user = user.Substring (i + 1);
var res = new NameValueCollection ();
res["username"] = user;
res["password"] = pass;
return res;
}
internal override string ToBasicString ()
{
var userPass = String.Format ("{0}:{1}", Parameters["username"], Parameters["password"]);
var cred = Convert.ToBase64String (Encoding.UTF8.GetBytes (userPass));
return "Basic " + cred;
}
internal override string ToDigestString ()
{
var output = new StringBuilder (256);
output.AppendFormat (
"Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"",
Parameters["username"],
Parameters["realm"],
Parameters["nonce"],
Parameters["uri"],
Parameters["response"]);
var opaque = Parameters["opaque"];
if (opaque != null)
output.AppendFormat (", opaque=\"{0}\"", opaque);
var algo = Parameters["algorithm"];
if (algo != null)
output.AppendFormat (", algorithm={0}", algo);
var qop = Parameters["qop"];
if (qop != null)
output.AppendFormat (
", qop={0}, cnonce=\"{1}\", nc={2}", qop, Parameters["cnonce"], Parameters["nc"]);
return output.ToString ();
}
#endregion
#region Public Methods
public IIdentity ToIdentity ()
{
var schm = Scheme;
return schm == AuthenticationSchemes.Basic
? new HttpBasicIdentity (Parameters["username"], Parameters["password"]) as IIdentity
: schm == AuthenticationSchemes.Digest
? new HttpDigestIdentity (Parameters)
: null;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a33bdedb4171f4a72bb87b346f3c11e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,66 @@
#region License
/*
* AuthenticationSchemes.cs
*
* This code is derived from AuthenticationSchemes.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Atsushi Enomoto <atsushi@ximian.com>
*/
#endregion
using System;
namespace WebSocketSharp.Net
{
/// <summary>
/// Specifies the scheme for authentication.
/// </summary>
public enum AuthenticationSchemes
{
/// <summary>
/// No authentication is allowed.
/// </summary>
None,
/// <summary>
/// Specifies digest authentication.
/// </summary>
Digest = 1,
/// <summary>
/// Specifies basic authentication.
/// </summary>
Basic = 8,
/// <summary>
/// Specifies anonymous authentication.
/// </summary>
Anonymous = 0x8000
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42e611894e95847c8895dc7672c60d15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,93 @@
#region License
/*
* Chunk.cs
*
* This code is derived from ChunkStream.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
* Copyright (c) 2014-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
*/
#endregion
using System;
namespace WebSocketSharp.Net
{
internal class Chunk
{
#region Private Fields
private byte[] _data;
private int _offset;
#endregion
#region Public Constructors
public Chunk (byte[] data)
{
_data = data;
}
#endregion
#region Public Properties
public int ReadLeft {
get {
return _data.Length - _offset;
}
}
#endregion
#region Public Methods
public int Read (byte[] buffer, int offset, int count)
{
var left = _data.Length - _offset;
if (left == 0)
return 0;
if (count > left)
count = left;
Buffer.BlockCopy (_data, _offset, buffer, offset, count);
_offset += count;
return count;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db88f6404ee684095ba5a0b4ab0b1b50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,383 @@
#region License
/*
* ChunkStream.cs
*
* This code is derived from ChunkStream.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
* Copyright (c) 2012-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
namespace WebSocketSharp.Net
{
internal class ChunkStream
{
#region Private Fields
private int _chunkRead;
private int _chunkSize;
private List<Chunk> _chunks;
private bool _gotIt;
private WebHeaderCollection _headers;
private StringBuilder _saved;
private bool _sawCr;
private InputChunkState _state;
private int _trailerState;
#endregion
#region Public Constructors
public ChunkStream (WebHeaderCollection headers)
{
_headers = headers;
_chunkSize = -1;
_chunks = new List<Chunk> ();
_saved = new StringBuilder ();
}
#endregion
#region Public Properties
public WebHeaderCollection Headers {
get {
return _headers;
}
}
public bool WantsMore {
get {
return _state < InputChunkState.End;
}
}
#endregion
#region Private Methods
private int read (byte[] buffer, int offset, int count)
{
var nread = 0;
var cnt = _chunks.Count;
for (var i = 0; i < cnt; i++) {
var chunk = _chunks[i];
if (chunk == null)
continue;
if (chunk.ReadLeft == 0) {
_chunks[i] = null;
continue;
}
nread += chunk.Read (buffer, offset + nread, count - nread);
if (nread == count)
break;
}
return nread;
}
private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
{
if (!_sawCr) {
if (buffer[offset++] != 13)
throwProtocolViolation ("CR is expected.");
_sawCr = true;
if (offset == length)
return InputChunkState.DataEnded;
}
if (buffer[offset++] != 10)
throwProtocolViolation ("LF is expected.");
return InputChunkState.None;
}
private InputChunkState setChunkSize (
byte[] buffer, ref int offset, int length
)
{
byte b = 0;
while (offset < length) {
b = buffer[offset++];
if (_sawCr) {
if (b != 10)
throwProtocolViolation ("LF is expected.");
break;
}
if (b == 13) {
_sawCr = true;
continue;
}
if (b == 10)
throwProtocolViolation ("LF is unexpected.");
if (_gotIt)
continue;
if (b == 32 || b == 59) { // SP or ';'
_gotIt = true;
continue;
}
_saved.Append ((char) b);
}
if (_saved.Length > 20)
throwProtocolViolation ("The chunk size is too big.");
if (b != 10)
return InputChunkState.None;
var s = _saved.ToString ();
try {
_chunkSize = Int32.Parse (s, NumberStyles.HexNumber);
}
catch {
throwProtocolViolation ("The chunk size cannot be parsed.");
}
_chunkRead = 0;
if (_chunkSize == 0) {
_trailerState = 2;
return InputChunkState.Trailer;
}
return InputChunkState.Data;
}
private InputChunkState setTrailer (
byte[] buffer, ref int offset, int length
)
{
while (offset < length) {
if (_trailerState == 4) // CR LF CR LF
break;
var b = buffer[offset++];
_saved.Append ((char) b);
if (_trailerState == 1 || _trailerState == 3) { // CR or CR LF CR
if (b != 10)
throwProtocolViolation ("LF is expected.");
_trailerState++;
continue;
}
if (b == 13) {
_trailerState++;
continue;
}
if (b == 10)
throwProtocolViolation ("LF is unexpected.");
_trailerState = 0;
}
var len = _saved.Length;
if (len > 4196)
throwProtocolViolation ("The trailer is too long.");
if (_trailerState < 4)
return InputChunkState.Trailer;
if (len == 2)
return InputChunkState.End;
_saved.Length = len - 2;
var val = _saved.ToString ();
var reader = new StringReader (val);
while (true) {
var line = reader.ReadLine ();
if (line == null || line.Length == 0)
break;
_headers.Add (line);
}
return InputChunkState.End;
}
private static void throwProtocolViolation (string message)
{
throw new WebException (
message, null, WebExceptionStatus.ServerProtocolViolation, null
);
}
private void write (byte[] buffer, int offset, int length)
{
if (_state == InputChunkState.End)
throwProtocolViolation ("The chunks were ended.");
if (_state == InputChunkState.None) {
_state = setChunkSize (buffer, ref offset, length);
if (_state == InputChunkState.None)
return;
_saved.Length = 0;
_sawCr = false;
_gotIt = false;
}
if (_state == InputChunkState.Data) {
if (offset >= length)
return;
_state = writeData (buffer, ref offset, length);
if (_state == InputChunkState.Data)
return;
}
if (_state == InputChunkState.DataEnded) {
if (offset >= length)
return;
_state = seekCrLf (buffer, ref offset, length);
if (_state == InputChunkState.DataEnded)
return;
_sawCr = false;
}
if (_state == InputChunkState.Trailer) {
if (offset >= length)
return;
_state = setTrailer (buffer, ref offset, length);
if (_state == InputChunkState.Trailer)
return;
_saved.Length = 0;
}
if (offset >= length)
return;
write (buffer, offset, length);
}
private InputChunkState writeData (
byte[] buffer, ref int offset, int length
)
{
var cnt = length - offset;
var left = _chunkSize - _chunkRead;
if (cnt > left)
cnt = left;
var data = new byte[cnt];
Buffer.BlockCopy (buffer, offset, data, 0, cnt);
var chunk = new Chunk (data);
_chunks.Add (chunk);
offset += cnt;
_chunkRead += cnt;
return _chunkRead == _chunkSize
? InputChunkState.DataEnded
: InputChunkState.Data;
}
#endregion
#region Internal Methods
internal void ResetBuffer ()
{
_chunkRead = 0;
_chunkSize = -1;
_chunks.Clear ();
}
#endregion
#region Public Methods
public int Read (byte[] buffer, int offset, int count)
{
if (count <= 0)
return 0;
return read (buffer, offset, count);
}
public void Write (byte[] buffer, int offset, int count)
{
if (count <= 0)
return;
write (buffer, offset, offset + count);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4839a977b3874235bb57c9c4a19ecb1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,249 @@
#region License
/*
* ChunkedRequestStream.cs
*
* This code is derived from ChunkedInputStream.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
using System;
using System.IO;
namespace WebSocketSharp.Net
{
internal class ChunkedRequestStream : RequestStream
{
#region Private Fields
private static readonly int _bufferLength;
private HttpListenerContext _context;
private ChunkStream _decoder;
private bool _disposed;
private bool _noMoreData;
#endregion
#region Static Constructor
static ChunkedRequestStream ()
{
_bufferLength = 8192;
}
#endregion
#region Internal Constructors
internal ChunkedRequestStream (
Stream stream,
byte[] buffer,
int offset,
int count,
HttpListenerContext context
)
: base (stream, buffer, offset, count, -1)
{
_context = context;
_decoder = new ChunkStream (
(WebHeaderCollection) context.Request.Headers
);
}
#endregion
#region Private Methods
private void onRead (IAsyncResult asyncResult)
{
var rstate = (ReadBufferState) asyncResult.AsyncState;
var ares = rstate.AsyncResult;
try {
var nread = base.EndRead (asyncResult);
_decoder.Write (ares.Buffer, ares.Offset, nread);
nread = _decoder.Read (rstate.Buffer, rstate.Offset, rstate.Count);
rstate.Offset += nread;
rstate.Count -= nread;
if (rstate.Count == 0 || !_decoder.WantsMore || nread == 0) {
_noMoreData = !_decoder.WantsMore && nread == 0;
ares.Count = rstate.InitialCount - rstate.Count;
ares.Complete ();
return;
}
base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate);
}
catch (Exception ex) {
_context.ErrorMessage = "I/O operation aborted";
_context.SendError ();
ares.Complete (ex);
}
}
#endregion
#region Public Methods
public override IAsyncResult BeginRead (
byte[] buffer, int offset, int count, AsyncCallback callback, object state
)
{
if (_disposed) {
var name = GetType ().ToString ();
throw new ObjectDisposedException (name);
}
if (buffer == null)
throw new ArgumentNullException ("buffer");
if (offset < 0) {
var msg = "A negative value.";
throw new ArgumentOutOfRangeException ("offset", msg);
}
if (count < 0) {
var msg = "A negative value.";
throw new ArgumentOutOfRangeException ("count", msg);
}
var len = buffer.Length;
if (offset + count > len) {
var msg = "The sum of 'offset' and 'count' is greater than the length of 'buffer'.";
throw new ArgumentException (msg);
}
var ares = new HttpStreamAsyncResult (callback, state);
if (_noMoreData) {
ares.Complete ();
return ares;
}
var nread = _decoder.Read (buffer, offset, count);
offset += nread;
count -= nread;
if (count == 0) {
ares.Count = nread;
ares.Complete ();
return ares;
}
if (!_decoder.WantsMore) {
_noMoreData = nread == 0;
ares.Count = nread;
ares.Complete ();
return ares;
}
ares.Buffer = new byte[_bufferLength];
ares.Offset = 0;
ares.Count = _bufferLength;
var rstate = new ReadBufferState (buffer, offset, count, ares);
rstate.InitialCount += nread;
base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate);
return ares;
}
public override void Close ()
{
if (_disposed)
return;
_disposed = true;
base.Close ();
}
public override int EndRead (IAsyncResult asyncResult)
{
if (_disposed) {
var name = GetType ().ToString ();
throw new ObjectDisposedException (name);
}
if (asyncResult == null)
throw new ArgumentNullException ("asyncResult");
var ares = asyncResult as HttpStreamAsyncResult;
if (ares == null) {
var msg = "A wrong IAsyncResult instance.";
throw new ArgumentException (msg, "asyncResult");
}
if (!ares.IsCompleted)
ares.AsyncWaitHandle.WaitOne ();
if (ares.HasException) {
var msg = "The I/O operation has been aborted.";
throw new HttpListenerException (995, msg);
}
return ares.Count;
}
public override int Read (byte[] buffer, int offset, int count)
{
var ares = BeginRead (buffer, offset, count, null, null);
return EndRead (ares);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71bd05d76f7ff4d89a94b93d69e2a5ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,303 @@
#region License
/*
* ClientSslConfiguration.cs
*
* The MIT License
*
* Copyright (c) 2014 liryna
* Copyright (c) 2014-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Liryna <liryna.stark@gmail.com>
*/
#endregion
using System;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
namespace WebSocketSharp.Net
{
/// <summary>
/// Stores the parameters for the <see cref="SslStream"/> used by clients.
/// </summary>
public class ClientSslConfiguration
{
#region Private Fields
private bool _checkCertRevocation;
private LocalCertificateSelectionCallback _clientCertSelectionCallback;
private X509CertificateCollection _clientCerts;
private SslProtocols _enabledSslProtocols;
private RemoteCertificateValidationCallback _serverCertValidationCallback;
private string _targetHost;
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ClientSslConfiguration"/>
/// class with the specified target host server name.
/// </summary>
/// <param name="targetHost">
/// A <see cref="string"/> that specifies the target host server name.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="targetHost"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="targetHost"/> is an empty string.
/// </exception>
public ClientSslConfiguration (string targetHost)
{
if (targetHost == null)
throw new ArgumentNullException ("targetHost");
if (targetHost.Length == 0)
throw new ArgumentException ("An empty string.", "targetHost");
_targetHost = targetHost;
_enabledSslProtocols = SslProtocols.None;
}
/// <summary>
/// Initializes a new instance of the <see cref="ClientSslConfiguration"/>
/// class that stores the parameters copied from the specified configuration.
/// </summary>
/// <param name="configuration">
/// A <see cref="ClientSslConfiguration"/> from which to copy.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="configuration"/> is <see langword="null"/>.
/// </exception>
public ClientSslConfiguration (ClientSslConfiguration configuration)
{
if (configuration == null)
throw new ArgumentNullException ("configuration");
_checkCertRevocation = configuration._checkCertRevocation;
_clientCertSelectionCallback = configuration._clientCertSelectionCallback;
_clientCerts = configuration._clientCerts;
_enabledSslProtocols = configuration._enabledSslProtocols;
_serverCertValidationCallback = configuration._serverCertValidationCallback;
_targetHost = configuration._targetHost;
}
#endregion
#region Public Properties
/// <summary>
/// Gets or sets a value indicating whether the certificate revocation
/// list is checked during authentication.
/// </summary>
/// <value>
/// <para>
/// <c>true</c> if the certificate revocation list is checked during
/// authentication; otherwise, <c>false</c>.
/// </para>
/// <para>
/// The default value is <c>false</c>.
/// </para>
/// </value>
public bool CheckCertificateRevocation {
get {
return _checkCertRevocation;
}
set {
_checkCertRevocation = value;
}
}
/// <summary>
/// Gets or sets the collection of client certificates from which to select
/// one to supply to the server.
/// </summary>
/// <value>
/// <para>
/// A <see cref="X509CertificateCollection"/> or <see langword="null"/>.
/// </para>
/// <para>
/// The collection contains client certificates from which to select.
/// </para>
/// <para>
/// The default value is <see langword="null"/>.
/// </para>
/// </value>
public X509CertificateCollection ClientCertificates {
get {
return _clientCerts;
}
set {
_clientCerts = value;
}
}
/// <summary>
/// Gets or sets the callback used to select the certificate to supply to
/// the server.
/// </summary>
/// <remarks>
/// No certificate is supplied if the callback returns <see langword="null"/>.
/// </remarks>
/// <value>
/// <para>
/// A <see cref="LocalCertificateSelectionCallback"/> delegate that
/// invokes the method called for selecting the certificate.
/// </para>
/// <para>
/// The default value is a delegate that invokes a method that only
/// returns <see langword="null"/>.
/// </para>
/// </value>
public LocalCertificateSelectionCallback ClientCertificateSelectionCallback {
get {
if (_clientCertSelectionCallback == null)
_clientCertSelectionCallback = defaultSelectClientCertificate;
return _clientCertSelectionCallback;
}
set {
_clientCertSelectionCallback = value;
}
}
/// <summary>
/// Gets or sets the protocols used for authentication.
/// </summary>
/// <value>
/// <para>
/// Any of the <see cref="SslProtocols"/> enum values.
/// </para>
/// <para>
/// It represents the protocols used for authentication.
/// </para>
/// <para>
/// The default value is <see cref="SslProtocols.None"/>.
/// </para>
/// </value>
public SslProtocols EnabledSslProtocols {
get {
return _enabledSslProtocols;
}
set {
_enabledSslProtocols = value;
}
}
/// <summary>
/// Gets or sets the callback used to validate the certificate supplied by
/// the server.
/// </summary>
/// <remarks>
/// The certificate is valid if the callback returns <c>true</c>.
/// </remarks>
/// <value>
/// <para>
/// A <see cref="RemoteCertificateValidationCallback"/> delegate that
/// invokes the method called for validating the certificate.
/// </para>
/// <para>
/// The default value is a delegate that invokes a method that only
/// returns <c>true</c>.
/// </para>
/// </value>
public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
get {
if (_serverCertValidationCallback == null)
_serverCertValidationCallback = defaultValidateServerCertificate;
return _serverCertValidationCallback;
}
set {
_serverCertValidationCallback = value;
}
}
/// <summary>
/// Gets or sets the target host server name.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the name of the server that
/// will share a secure connection with a client.
/// </value>
/// <exception cref="ArgumentNullException">
/// The value specified for a set operation is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// The value specified for a set operation is an empty string.
/// </exception>
public string TargetHost {
get {
return _targetHost;
}
set {
if (value == null)
throw new ArgumentNullException ("value");
if (value.Length == 0)
throw new ArgumentException ("An empty string.", "value");
_targetHost = value;
}
}
#endregion
#region Private Methods
private static X509Certificate defaultSelectClientCertificate (
object sender,
string targetHost,
X509CertificateCollection clientCertificates,
X509Certificate serverCertificate,
string[] acceptableIssuers
)
{
return null;
}
private static bool defaultValidateServerCertificate (
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors
)
{
return true;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91da23afcc8474a69b8e3a3105d9ca4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1414df5fe386431c8c0c555bc27049b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,821 @@
#region License
/*
* CookieCollection.cs
*
* This code is derived from CookieCollection.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2019 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Lawrence Pit <loz@cable.a2000.nl>
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
* - Sebastien Pouliot <sebastien@ximian.com>
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace WebSocketSharp.Net
{
/// <summary>
/// Provides a collection of instances of the <see cref="Cookie"/> class.
/// </summary>
[Serializable]
public class CookieCollection : ICollection<Cookie>
{
#region Private Fields
private List<Cookie> _list;
private bool _readOnly;
private object _sync;
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="CookieCollection"/> class.
/// </summary>
public CookieCollection ()
{
_list = new List<Cookie> ();
_sync = ((ICollection) _list).SyncRoot;
}
#endregion
#region Internal Properties
internal IList<Cookie> List {
get {
return _list;
}
}
internal IEnumerable<Cookie> Sorted {
get {
var list = new List<Cookie> (_list);
if (list.Count > 1)
list.Sort (compareForSorted);
return list;
}
}
#endregion
#region Public Properties
/// <summary>
/// Gets the number of cookies in the collection.
/// </summary>
/// <value>
/// An <see cref="int"/> that represents the number of cookies in
/// the collection.
/// </value>
public int Count {
get {
return _list.Count;
}
}
/// <summary>
/// Gets a value indicating whether the collection is read-only.
/// </summary>
/// <value>
/// <para>
/// <c>true</c> if the collection is read-only; otherwise, <c>false</c>.
/// </para>
/// <para>
/// The default value is <c>false</c>.
/// </para>
/// </value>
public bool IsReadOnly {
get {
return _readOnly;
}
internal set {
_readOnly = value;
}
}
/// <summary>
/// Gets a value indicating whether the access to the collection is
/// thread safe.
/// </summary>
/// <value>
/// <para>
/// <c>true</c> if the access to the collection is thread safe;
/// otherwise, <c>false</c>.
/// </para>
/// <para>
/// The default value is <c>false</c>.
/// </para>
/// </value>
public bool IsSynchronized {
get {
return false;
}
}
/// <summary>
/// Gets the cookie at the specified index from the collection.
/// </summary>
/// <value>
/// A <see cref="Cookie"/> at the specified index in the collection.
/// </value>
/// <param name="index">
/// An <see cref="int"/> that specifies the zero-based index of the cookie
/// to find.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="index"/> is out of allowable range for the collection.
/// </exception>
public Cookie this[int index] {
get {
if (index < 0 || index >= _list.Count)
throw new ArgumentOutOfRangeException ("index");
return _list[index];
}
}
/// <summary>
/// Gets the cookie with the specified name from the collection.
/// </summary>
/// <value>
/// <para>
/// A <see cref="Cookie"/> with the specified name in the collection.
/// </para>
/// <para>
/// <see langword="null"/> if not found.
/// </para>
/// </value>
/// <param name="name">
/// A <see cref="string"/> that specifies the name of the cookie to find.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <see langword="null"/>.
/// </exception>
public Cookie this[string name] {
get {
if (name == null)
throw new ArgumentNullException ("name");
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
foreach (var cookie in Sorted) {
if (cookie.Name.Equals (name, caseInsensitive))
return cookie;
}
return null;
}
}
/// <summary>
/// Gets an object used to synchronize access to the collection.
/// </summary>
/// <value>
/// An <see cref="object"/> used to synchronize access to the collection.
/// </value>
public object SyncRoot {
get {
return _sync;
}
}
#endregion
#region Private Methods
private void add (Cookie cookie)
{
var idx = search (cookie);
if (idx == -1) {
_list.Add (cookie);
return;
}
_list[idx] = cookie;
}
private static int compareForSort (Cookie x, Cookie y)
{
return (x.Name.Length + x.Value.Length)
- (y.Name.Length + y.Value.Length);
}
private static int compareForSorted (Cookie x, Cookie y)
{
var ret = x.Version - y.Version;
return ret != 0
? ret
: (ret = x.Name.CompareTo (y.Name)) != 0
? ret
: y.Path.Length - x.Path.Length;
}
private static CookieCollection parseRequest (string value)
{
var ret = new CookieCollection ();
Cookie cookie = null;
var ver = 0;
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
var pairs = value.SplitHeaderValue (',', ';').ToList ();
for (var i = 0; i < pairs.Count; i++) {
var pair = pairs[i].Trim ();
if (pair.Length == 0)
continue;
var idx = pair.IndexOf ('=');
if (idx == -1) {
if (cookie == null)
continue;
if (pair.Equals ("$port", caseInsensitive)) {
cookie.Port = "\"\"";
continue;
}
continue;
}
if (idx == 0) {
if (cookie != null) {
ret.add (cookie);
cookie = null;
}
continue;
}
var name = pair.Substring (0, idx).TrimEnd (' ');
var val = idx < pair.Length - 1
? pair.Substring (idx + 1).TrimStart (' ')
: String.Empty;
if (name.Equals ("$version", caseInsensitive)) {
if (val.Length == 0)
continue;
int num;
if (!Int32.TryParse (val.Unquote (), out num))
continue;
ver = num;
continue;
}
if (name.Equals ("$path", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Path = val;
continue;
}
if (name.Equals ("$domain", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Domain = val;
continue;
}
if (name.Equals ("$port", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Port = val;
continue;
}
if (cookie != null)
ret.add (cookie);
if (!Cookie.TryCreate (name, val, out cookie))
continue;
if (ver != 0)
cookie.Version = ver;
}
if (cookie != null)
ret.add (cookie);
return ret;
}
private static CookieCollection parseResponse (string value)
{
var ret = new CookieCollection ();
Cookie cookie = null;
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
var pairs = value.SplitHeaderValue (',', ';').ToList ();
for (var i = 0; i < pairs.Count; i++) {
var pair = pairs[i].Trim ();
if (pair.Length == 0)
continue;
var idx = pair.IndexOf ('=');
if (idx == -1) {
if (cookie == null)
continue;
if (pair.Equals ("port", caseInsensitive)) {
cookie.Port = "\"\"";
continue;
}
if (pair.Equals ("discard", caseInsensitive)) {
cookie.Discard = true;
continue;
}
if (pair.Equals ("secure", caseInsensitive)) {
cookie.Secure = true;
continue;
}
if (pair.Equals ("httponly", caseInsensitive)) {
cookie.HttpOnly = true;
continue;
}
continue;
}
if (idx == 0) {
if (cookie != null) {
ret.add (cookie);
cookie = null;
}
continue;
}
var name = pair.Substring (0, idx).TrimEnd (' ');
var val = idx < pair.Length - 1
? pair.Substring (idx + 1).TrimStart (' ')
: String.Empty;
if (name.Equals ("version", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
int num;
if (!Int32.TryParse (val.Unquote (), out num))
continue;
cookie.Version = num;
continue;
}
if (name.Equals ("expires", caseInsensitive)) {
if (val.Length == 0)
continue;
if (i == pairs.Count - 1)
break;
i++;
if (cookie == null)
continue;
if (cookie.Expires != DateTime.MinValue)
continue;
var buff = new StringBuilder (val, 32);
buff.AppendFormat (", {0}", pairs[i].Trim ());
DateTime expires;
if (
!DateTime.TryParseExact (
buff.ToString (),
new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" },
CultureInfo.CreateSpecificCulture ("en-US"),
DateTimeStyles.AdjustToUniversal
| DateTimeStyles.AssumeUniversal,
out expires
)
)
continue;
cookie.Expires = expires.ToLocalTime ();
continue;
}
if (name.Equals ("max-age", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
int num;
if (!Int32.TryParse (val.Unquote (), out num))
continue;
cookie.MaxAge = num;
continue;
}
if (name.Equals ("path", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Path = val;
continue;
}
if (name.Equals ("domain", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Domain = val;
continue;
}
if (name.Equals ("port", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Port = val;
continue;
}
if (name.Equals ("comment", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.Comment = urlDecode (val, Encoding.UTF8);
continue;
}
if (name.Equals ("commenturl", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.CommentUri = val.Unquote ().ToUri ();
continue;
}
if (name.Equals ("samesite", caseInsensitive)) {
if (cookie == null)
continue;
if (val.Length == 0)
continue;
cookie.SameSite = val.Unquote ();
continue;
}
if (cookie != null)
ret.add (cookie);
Cookie.TryCreate (name, val, out cookie);
}
if (cookie != null)
ret.add (cookie);
return ret;
}
private int search (Cookie cookie)
{
for (var i = _list.Count - 1; i >= 0; i--) {
if (_list[i].EqualsWithoutValue (cookie))
return i;
}
return -1;
}
private static string urlDecode (string s, Encoding encoding)
{
if (s.IndexOfAny (new[] { '%', '+' }) == -1)
return s;
try {
return HttpUtility.UrlDecode (s, encoding);
}
catch {
return null;
}
}
#endregion
#region Internal Methods
internal static CookieCollection Parse (string value, bool response)
{
try {
return response
? parseResponse (value)
: parseRequest (value);
}
catch (Exception ex) {
throw new CookieException ("It could not be parsed.", ex);
}
}
internal void SetOrRemove (Cookie cookie)
{
var idx = search (cookie);
if (idx == -1) {
if (cookie.Expired)
return;
_list.Add (cookie);
return;
}
if (cookie.Expired) {
_list.RemoveAt (idx);
return;
}
_list[idx] = cookie;
}
internal void SetOrRemove (CookieCollection cookies)
{
foreach (var cookie in cookies._list)
SetOrRemove (cookie);
}
internal void Sort ()
{
if (_list.Count > 1)
_list.Sort (compareForSort);
}
#endregion
#region Public Methods
/// <summary>
/// Adds the specified cookie to the collection.
/// </summary>
/// <param name="cookie">
/// A <see cref="Cookie"/> to add.
/// </param>
/// <exception cref="InvalidOperationException">
/// The collection is read-only.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="cookie"/> is <see langword="null"/>.
/// </exception>
public void Add (Cookie cookie)
{
if (_readOnly) {
var msg = "The collection is read-only.";
throw new InvalidOperationException (msg);
}
if (cookie == null)
throw new ArgumentNullException ("cookie");
add (cookie);
}
/// <summary>
/// Adds the specified cookies to the collection.
/// </summary>
/// <param name="cookies">
/// A <see cref="CookieCollection"/> that contains the cookies to add.
/// </param>
/// <exception cref="InvalidOperationException">
/// The collection is read-only.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="cookies"/> is <see langword="null"/>.
/// </exception>
public void Add (CookieCollection cookies)
{
if (_readOnly) {
var msg = "The collection is read-only.";
throw new InvalidOperationException (msg);
}
if (cookies == null)
throw new ArgumentNullException ("cookies");
foreach (var cookie in cookies._list)
add (cookie);
}
/// <summary>
/// Removes all cookies from the collection.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The collection is read-only.
/// </exception>
public void Clear ()
{
if (_readOnly) {
var msg = "The collection is read-only.";
throw new InvalidOperationException (msg);
}
_list.Clear ();
}
/// <summary>
/// Determines whether the collection contains the specified cookie.
/// </summary>
/// <returns>
/// <c>true</c> if the cookie is found in the collection; otherwise,
/// <c>false</c>.
/// </returns>
/// <param name="cookie">
/// A <see cref="Cookie"/> to find.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="cookie"/> is <see langword="null"/>.
/// </exception>
public bool Contains (Cookie cookie)
{
if (cookie == null)
throw new ArgumentNullException ("cookie");
return search (cookie) > -1;
}
/// <summary>
/// Copies the elements of the collection to the specified array,
/// starting at the specified index.
/// </summary>
/// <param name="array">
/// An array of <see cref="Cookie"/> that specifies the destination of
/// the elements copied from the collection.
/// </param>
/// <param name="index">
/// An <see cref="int"/> that specifies the zero-based index in
/// the array at which copying starts.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="array"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="index"/> is less than zero.
/// </exception>
/// <exception cref="ArgumentException">
/// The space from <paramref name="index"/> to the end of
/// <paramref name="array"/> is not enough to copy to.
/// </exception>
public void CopyTo (Cookie[] array, int index)
{
if (array == null)
throw new ArgumentNullException ("array");
if (index < 0)
throw new ArgumentOutOfRangeException ("index", "Less than zero.");
if (array.Length - index < _list.Count) {
var msg = "The available space of the array is not enough to copy to.";
throw new ArgumentException (msg);
}
_list.CopyTo (array, index);
}
/// <summary>
/// Gets the enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.Generic.IEnumerator{Cookie}"/>
/// instance that can be used to iterate through the collection.
/// </returns>
public IEnumerator<Cookie> GetEnumerator ()
{
return _list.GetEnumerator ();
}
/// <summary>
/// Removes the specified cookie from the collection.
/// </summary>
/// <returns>
/// <para>
/// <c>true</c> if the cookie is successfully removed; otherwise,
/// <c>false</c>.
/// </para>
/// <para>
/// <c>false</c> if the cookie is not found in the collection.
/// </para>
/// </returns>
/// <param name="cookie">
/// A <see cref="Cookie"/> to remove.
/// </param>
/// <exception cref="InvalidOperationException">
/// The collection is read-only.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="cookie"/> is <see langword="null"/>.
/// </exception>
public bool Remove (Cookie cookie)
{
if (_readOnly) {
var msg = "The collection is read-only.";
throw new InvalidOperationException (msg);
}
if (cookie == null)
throw new ArgumentNullException ("cookie");
var idx = search (cookie);
if (idx == -1)
return false;
_list.RemoveAt (idx);
return true;
}
#endregion
#region Explicit Interface Implementations
/// <summary>
/// Gets the enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// An <see cref="IEnumerator"/> instance that can be used to iterate
/// through the collection.
/// </returns>
IEnumerator IEnumerable.GetEnumerator ()
{
return _list.GetEnumerator ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7b7b01a9c8124c788f268c8c869fe9e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,165 @@
#region License
/*
* CookieException.cs
*
* This code is derived from CookieException.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2012-2019 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Lawrence Pit <loz@cable.a2000.nl>
*/
#endregion
using System;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace WebSocketSharp.Net
{
/// <summary>
/// The exception that is thrown when a <see cref="Cookie"/> gets an error.
/// </summary>
[Serializable]
public class CookieException : FormatException, ISerializable
{
#region Internal Constructors
internal CookieException (string message)
: base (message)
{
}
internal CookieException (string message, Exception innerException)
: base (message, innerException)
{
}
#endregion
#region Protected Constructors
/// <summary>
/// Initializes a new instance of the <see cref="CookieException"/> class
/// with the serialized data.
/// </summary>
/// <param name="serializationInfo">
/// A <see cref="SerializationInfo"/> that holds the serialized object data.
/// </param>
/// <param name="streamingContext">
/// A <see cref="StreamingContext"/> that specifies the source for
/// the deserialization.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="serializationInfo"/> is <see langword="null"/>.
/// </exception>
protected CookieException (
SerializationInfo serializationInfo, StreamingContext streamingContext
)
: base (serializationInfo, streamingContext)
{
}
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="CookieException"/> class.
/// </summary>
public CookieException ()
: base ()
{
}
#endregion
#region Public Methods
/// <summary>
/// Populates the specified <see cref="SerializationInfo"/> instance with
/// the data needed to serialize the current instance.
/// </summary>
/// <param name="serializationInfo">
/// A <see cref="SerializationInfo"/> that holds the serialized object data.
/// </param>
/// <param name="streamingContext">
/// A <see cref="StreamingContext"/> that specifies the destination for
/// the serialization.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="serializationInfo"/> is <see langword="null"/>.
/// </exception>
[
SecurityPermission (
SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.SerializationFormatter
)
]
public override void GetObjectData (
SerializationInfo serializationInfo, StreamingContext streamingContext
)
{
base.GetObjectData (serializationInfo, streamingContext);
}
#endregion
#region Explicit Interface Implementation
/// <summary>
/// Populates the specified <see cref="SerializationInfo"/> instance with
/// the data needed to serialize the current instance.
/// </summary>
/// <param name="serializationInfo">
/// A <see cref="SerializationInfo"/> that holds the serialized object data.
/// </param>
/// <param name="streamingContext">
/// A <see cref="StreamingContext"/> that specifies the destination for
/// the serialization.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="serializationInfo"/> is <see langword="null"/>.
/// </exception>
[
SecurityPermission (
SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.SerializationFormatter,
SerializationFormatter = true
)
]
void ISerializable.GetObjectData (
SerializationInfo serializationInfo, StreamingContext streamingContext
)
{
base.GetObjectData (serializationInfo, streamingContext);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c3d26e61b20d4ac497ba1a553c6a47c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,582 @@
#region License
/*
* EndPointListener.cs
*
* This code is derived from EndPointListener.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
#region Contributors
/*
* Contributors:
* - Liryna <liryna.stark@gmail.com>
* - Nicholas Devenish
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
namespace WebSocketSharp.Net
{
internal sealed class EndPointListener
{
#region Private Fields
private List<HttpListenerPrefix> _all; // host == '+'
private Dictionary<HttpConnection, HttpConnection> _connections;
private object _connectionsSync;
private static readonly string _defaultCertFolderPath;
private IPEndPoint _endpoint;
private List<HttpListenerPrefix> _prefixes;
private bool _secure;
private Socket _socket;
private ServerSslConfiguration _sslConfig;
private List<HttpListenerPrefix> _unhandled; // host == '*'
#endregion
#region Static Constructor
static EndPointListener ()
{
_defaultCertFolderPath = Environment.GetFolderPath (
Environment.SpecialFolder.ApplicationData
);
}
#endregion
#region Internal Constructors
internal EndPointListener (
IPEndPoint endpoint,
bool secure,
string certificateFolderPath,
ServerSslConfiguration sslConfig,
bool reuseAddress
)
{
_endpoint = endpoint;
if (secure) {
var cert = getCertificate (
endpoint.Port,
certificateFolderPath,
sslConfig.ServerCertificate
);
if (cert == null) {
var msg = "No server certificate could be found.";
throw new ArgumentException (msg);
}
_secure = true;
_sslConfig = new ServerSslConfiguration (sslConfig);
_sslConfig.ServerCertificate = cert;
}
_prefixes = new List<HttpListenerPrefix> ();
_connections = new Dictionary<HttpConnection, HttpConnection> ();
_connectionsSync = ((ICollection) _connections).SyncRoot;
_socket = new Socket (
endpoint.Address.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp
);
if (reuseAddress) {
_socket.SetSocketOption (
SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress,
true
);
}
_socket.Bind (endpoint);
_socket.Listen (500);
_socket.BeginAccept (onAccept, this);
}
#endregion
#region Public Properties
public IPAddress Address {
get {
return _endpoint.Address;
}
}
public bool IsSecure {
get {
return _secure;
}
}
public int Port {
get {
return _endpoint.Port;
}
}
public ServerSslConfiguration SslConfiguration {
get {
return _sslConfig;
}
}
#endregion
#region Private Methods
private static void addSpecial (
List<HttpListenerPrefix> prefixes, HttpListenerPrefix prefix
)
{
var path = prefix.Path;
foreach (var pref in prefixes) {
if (pref.Path == path) {
var msg = "The prefix is already in use.";
throw new HttpListenerException (87, msg);
}
}
prefixes.Add (prefix);
}
private void clearConnections ()
{
HttpConnection[] conns = null;
lock (_connectionsSync) {
var cnt = _connections.Count;
if (cnt == 0)
return;
conns = new HttpConnection[cnt];
var vals = _connections.Values;
vals.CopyTo (conns, 0);
_connections.Clear ();
}
foreach (var conn in conns)
conn.Close (true);
}
private static RSACryptoServiceProvider createRSAFromFile (string path)
{
var rsa = new RSACryptoServiceProvider ();
var key = File.ReadAllBytes (path);
rsa.ImportCspBlob (key);
return rsa;
}
private static X509Certificate2 getCertificate (
int port, string folderPath, X509Certificate2 defaultCertificate
)
{
if (folderPath == null || folderPath.Length == 0)
folderPath = _defaultCertFolderPath;
try {
var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port));
var key = Path.Combine (folderPath, String.Format ("{0}.key", port));
if (File.Exists (cer) && File.Exists (key)) {
var cert = new X509Certificate2 (cer);
cert.PrivateKey = createRSAFromFile (key);
return cert;
}
}
catch {
}
return defaultCertificate;
}
private void leaveIfNoPrefix ()
{
if (_prefixes.Count > 0)
return;
var prefs = _unhandled;
if (prefs != null && prefs.Count > 0)
return;
prefs = _all;
if (prefs != null && prefs.Count > 0)
return;
Close ();
}
private static void onAccept (IAsyncResult asyncResult)
{
var lsnr = (EndPointListener) asyncResult.AsyncState;
Socket sock = null;
try {
sock = lsnr._socket.EndAccept (asyncResult);
}
catch (ObjectDisposedException) {
return;
}
catch (Exception) {
// TODO: Logging.
}
try {
lsnr._socket.BeginAccept (onAccept, lsnr);
}
catch (Exception) {
// TODO: Logging.
if (sock != null)
sock.Close ();
return;
}
if (sock == null)
return;
processAccepted (sock, lsnr);
}
private static void processAccepted (
Socket socket, EndPointListener listener
)
{
HttpConnection conn = null;
try {
conn = new HttpConnection (socket, listener);
}
catch (Exception) {
// TODO: Logging.
socket.Close ();
return;
}
lock (listener._connectionsSync)
listener._connections.Add (conn, conn);
conn.BeginReadRequest ();
}
private static bool removeSpecial (
List<HttpListenerPrefix> prefixes, HttpListenerPrefix prefix
)
{
var path = prefix.Path;
var cnt = prefixes.Count;
for (var i = 0; i < cnt; i++) {
if (prefixes[i].Path == path) {
prefixes.RemoveAt (i);
return true;
}
}
return false;
}
private static HttpListener searchHttpListenerFromSpecial (
string path, List<HttpListenerPrefix> prefixes
)
{
if (prefixes == null)
return null;
HttpListener ret = null;
var bestLen = -1;
foreach (var pref in prefixes) {
var prefPath = pref.Path;
var len = prefPath.Length;
if (len < bestLen)
continue;
if (path.StartsWith (prefPath, StringComparison.Ordinal)) {
bestLen = len;
ret = pref.Listener;
}
}
return ret;
}
#endregion
#region Internal Methods
internal static bool CertificateExists (int port, string folderPath)
{
if (folderPath == null || folderPath.Length == 0)
folderPath = _defaultCertFolderPath;
var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port));
var key = Path.Combine (folderPath, String.Format ("{0}.key", port));
return File.Exists (cer) && File.Exists (key);
}
internal void RemoveConnection (HttpConnection connection)
{
lock (_connectionsSync)
_connections.Remove (connection);
}
internal bool TrySearchHttpListener (Uri uri, out HttpListener listener)
{
listener = null;
if (uri == null)
return false;
var host = uri.Host;
var dns = Uri.CheckHostName (host) == UriHostNameType.Dns;
var port = uri.Port.ToString ();
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
if (path[path.Length - 1] != '/')
path += "/";
if (host != null && host.Length > 0) {
var prefs = _prefixes;
var bestLen = -1;
foreach (var pref in prefs) {
if (dns) {
var prefHost = pref.Host;
var prefDns = Uri.CheckHostName (prefHost) == UriHostNameType.Dns;
if (prefDns) {
if (prefHost != host)
continue;
}
}
if (pref.Port != port)
continue;
var prefPath = pref.Path;
var len = prefPath.Length;
if (len < bestLen)
continue;
if (path.StartsWith (prefPath, StringComparison.Ordinal)) {
bestLen = len;
listener = pref.Listener;
}
}
if (bestLen != -1)
return true;
}
listener = searchHttpListenerFromSpecial (path, _unhandled);
if (listener != null)
return true;
listener = searchHttpListenerFromSpecial (path, _all);
return listener != null;
}
#endregion
#region Public Methods
public void AddPrefix (HttpListenerPrefix prefix)
{
List<HttpListenerPrefix> current, future;
if (prefix.Host == "*") {
do {
current = _unhandled;
future = current != null
? new List<HttpListenerPrefix> (current)
: new List<HttpListenerPrefix> ();
addSpecial (future, prefix);
}
while (
Interlocked.CompareExchange (ref _unhandled, future, current) != current
);
return;
}
if (prefix.Host == "+") {
do {
current = _all;
future = current != null
? new List<HttpListenerPrefix> (current)
: new List<HttpListenerPrefix> ();
addSpecial (future, prefix);
}
while (
Interlocked.CompareExchange (ref _all, future, current) != current
);
return;
}
do {
current = _prefixes;
var idx = current.IndexOf (prefix);
if (idx > -1) {
if (current[idx].Listener != prefix.Listener) {
var msg = String.Format (
"There is another listener for {0}.", prefix
);
throw new HttpListenerException (87, msg);
}
return;
}
future = new List<HttpListenerPrefix> (current);
future.Add (prefix);
}
while (
Interlocked.CompareExchange (ref _prefixes, future, current) != current
);
}
public void Close ()
{
_socket.Close ();
clearConnections ();
EndPointManager.RemoveEndPoint (_endpoint);
}
public void RemovePrefix (HttpListenerPrefix prefix)
{
List<HttpListenerPrefix> current, future;
if (prefix.Host == "*") {
do {
current = _unhandled;
if (current == null)
break;
future = new List<HttpListenerPrefix> (current);
if (!removeSpecial (future, prefix))
break;
}
while (
Interlocked.CompareExchange (ref _unhandled, future, current) != current
);
leaveIfNoPrefix ();
return;
}
if (prefix.Host == "+") {
do {
current = _all;
if (current == null)
break;
future = new List<HttpListenerPrefix> (current);
if (!removeSpecial (future, prefix))
break;
}
while (
Interlocked.CompareExchange (ref _all, future, current) != current
);
leaveIfNoPrefix ();
return;
}
do {
current = _prefixes;
if (!current.Contains (prefix))
break;
future = new List<HttpListenerPrefix> (current);
future.Remove (prefix);
}
while (
Interlocked.CompareExchange (ref _prefixes, future, current) != current
);
leaveIfNoPrefix ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 684001ea2c1fb4201b110d24b256cfbd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,261 @@
#region License
/*
* EndPointManager.cs
*
* This code is derived from EndPointManager.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
*/
#endregion
#region Contributors
/*
* Contributors:
* - Liryna <liryna.stark@gmail.com>
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
namespace WebSocketSharp.Net
{
internal sealed class EndPointManager
{
#region Private Fields
private static readonly Dictionary<IPEndPoint, EndPointListener> _endpoints;
#endregion
#region Static Constructor
static EndPointManager ()
{
_endpoints = new Dictionary<IPEndPoint, EndPointListener> ();
}
#endregion
#region Private Constructors
private EndPointManager ()
{
}
#endregion
#region Private Methods
private static void addPrefix (string uriPrefix, HttpListener listener)
{
var pref = new HttpListenerPrefix (uriPrefix, listener);
var addr = convertToIPAddress (pref.Host);
if (addr == null) {
var msg = "The URI prefix includes an invalid host.";
throw new HttpListenerException (87, msg);
}
if (!addr.IsLocal ()) {
var msg = "The URI prefix includes an invalid host.";
throw new HttpListenerException (87, msg);
}
int port;
if (!Int32.TryParse (pref.Port, out port)) {
var msg = "The URI prefix includes an invalid port.";
throw new HttpListenerException (87, msg);
}
if (!port.IsPortNumber ()) {
var msg = "The URI prefix includes an invalid port.";
throw new HttpListenerException (87, msg);
}
var path = pref.Path;
if (path.IndexOf ('%') != -1) {
var msg = "The URI prefix includes an invalid path.";
throw new HttpListenerException (87, msg);
}
if (path.IndexOf ("//", StringComparison.Ordinal) != -1) {
var msg = "The URI prefix includes an invalid path.";
throw new HttpListenerException (87, msg);
}
var endpoint = new IPEndPoint (addr, port);
EndPointListener lsnr;
if (_endpoints.TryGetValue (endpoint, out lsnr)) {
if (lsnr.IsSecure ^ pref.IsSecure) {
var msg = "The URI prefix includes an invalid scheme.";
throw new HttpListenerException (87, msg);
}
}
else {
lsnr = new EndPointListener (
endpoint,
pref.IsSecure,
listener.CertificateFolderPath,
listener.SslConfiguration,
listener.ReuseAddress
);
_endpoints.Add (endpoint, lsnr);
}
lsnr.AddPrefix (pref);
}
private static IPAddress convertToIPAddress (string hostname)
{
if (hostname == "*")
return IPAddress.Any;
if (hostname == "+")
return IPAddress.Any;
return hostname.ToIPAddress ();
}
private static void removePrefix (string uriPrefix, HttpListener listener)
{
var pref = new HttpListenerPrefix (uriPrefix, listener);
var addr = convertToIPAddress (pref.Host);
if (addr == null)
return;
if (!addr.IsLocal ())
return;
int port;
if (!Int32.TryParse (pref.Port, out port))
return;
if (!port.IsPortNumber ())
return;
var path = pref.Path;
if (path.IndexOf ('%') != -1)
return;
if (path.IndexOf ("//", StringComparison.Ordinal) != -1)
return;
var endpoint = new IPEndPoint (addr, port);
EndPointListener lsnr;
if (!_endpoints.TryGetValue (endpoint, out lsnr))
return;
if (lsnr.IsSecure ^ pref.IsSecure)
return;
lsnr.RemovePrefix (pref);
}
#endregion
#region Internal Methods
internal static bool RemoveEndPoint (IPEndPoint endpoint)
{
lock (((ICollection) _endpoints).SyncRoot)
return _endpoints.Remove (endpoint);
}
#endregion
#region Public Methods
public static void AddListener (HttpListener listener)
{
var added = new List<string> ();
lock (((ICollection) _endpoints).SyncRoot) {
try {
foreach (var pref in listener.Prefixes) {
addPrefix (pref, listener);
added.Add (pref);
}
}
catch {
foreach (var pref in added)
removePrefix (pref, listener);
throw;
}
}
}
public static void AddPrefix (string uriPrefix, HttpListener listener)
{
lock (((ICollection) _endpoints).SyncRoot)
addPrefix (uriPrefix, listener);
}
public static void RemoveListener (HttpListener listener)
{
lock (((ICollection) _endpoints).SyncRoot) {
foreach (var pref in listener.Prefixes)
removePrefix (pref, listener);
}
}
public static void RemovePrefix (string uriPrefix, HttpListener listener)
{
lock (((ICollection) _endpoints).SyncRoot)
removePrefix (uriPrefix, listener);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 241b17e1eddde4e2886df45621e51052
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,82 @@
#region License
/*
* HttpBasicIdentity.cs
*
* This code is derived from HttpListenerBasicIdentity.cs (System.Net) of
* Mono (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2014-2017 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
using System;
using System.Security.Principal;
namespace WebSocketSharp.Net
{
/// <summary>
/// Holds the username and password from an HTTP Basic authentication attempt.
/// </summary>
public class HttpBasicIdentity : GenericIdentity
{
#region Private Fields
private string _password;
#endregion
#region Internal Constructors
internal HttpBasicIdentity (string username, string password)
: base (username, "Basic")
{
_password = password;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the password from a basic authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the password.
/// </value>
public virtual string Password {
get {
return _password;
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3cdcf69cbfaf74ad4978ed2a2045c3f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,571 @@
#region License
/*
* HttpConnection.cs
*
* This code is derived from HttpConnection.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
#region Contributors
/*
* Contributors:
* - Liryna <liryna.stark@gmail.com>
* - Rohan Singh <rohan-singh@hotmail.com>
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace WebSocketSharp.Net
{
internal sealed class HttpConnection
{
#region Private Fields
private int _attempts;
private byte[] _buffer;
private static readonly int _bufferLength;
private HttpListenerContext _context;
private StringBuilder _currentLine;
private InputState _inputState;
private RequestStream _inputStream;
private LineState _lineState;
private EndPointListener _listener;
private EndPoint _localEndPoint;
private static readonly int _maxInputLength;
private ResponseStream _outputStream;
private int _position;
private EndPoint _remoteEndPoint;
private MemoryStream _requestBuffer;
private int _reuses;
private bool _secure;
private Socket _socket;
private Stream _stream;
private object _sync;
private int _timeout;
private Dictionary<int, bool> _timeoutCanceled;
private Timer _timer;
#endregion
#region Static Constructor
static HttpConnection ()
{
_bufferLength = 8192;
_maxInputLength = 32768;
}
#endregion
#region Internal Constructors
internal HttpConnection (Socket socket, EndPointListener listener)
{
_socket = socket;
_listener = listener;
var netStream = new NetworkStream (socket, false);
if (listener.IsSecure) {
var sslConf = listener.SslConfiguration;
var sslStream = new SslStream (
netStream,
false,
sslConf.ClientCertificateValidationCallback
);
sslStream.AuthenticateAsServer (
sslConf.ServerCertificate,
sslConf.ClientCertificateRequired,
sslConf.EnabledSslProtocols,
sslConf.CheckCertificateRevocation
);
_secure = true;
_stream = sslStream;
}
else {
_stream = netStream;
}
_buffer = new byte[_bufferLength];
_localEndPoint = socket.LocalEndPoint;
_remoteEndPoint = socket.RemoteEndPoint;
_sync = new object ();
_timeoutCanceled = new Dictionary<int, bool> ();
_timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite);
init (90000); // 90k ms for first request, 15k ms from then on.
}
#endregion
#region Public Properties
public bool IsClosed {
get {
return _socket == null;
}
}
public bool IsLocal {
get {
return ((IPEndPoint) _remoteEndPoint).Address.IsLocal ();
}
}
public bool IsSecure {
get {
return _secure;
}
}
public IPEndPoint LocalEndPoint {
get {
return (IPEndPoint) _localEndPoint;
}
}
public IPEndPoint RemoteEndPoint {
get {
return (IPEndPoint) _remoteEndPoint;
}
}
public int Reuses {
get {
return _reuses;
}
}
public Stream Stream {
get {
return _stream;
}
}
#endregion
#region Private Methods
private void close ()
{
lock (_sync) {
if (_socket == null)
return;
disposeTimer ();
disposeRequestBuffer ();
disposeStream ();
closeSocket ();
}
_context.Unregister ();
_listener.RemoveConnection (this);
}
private void closeSocket ()
{
try {
_socket.Shutdown (SocketShutdown.Both);
}
catch {
}
_socket.Close ();
_socket = null;
}
private void disposeRequestBuffer ()
{
if (_requestBuffer == null)
return;
_requestBuffer.Dispose ();
_requestBuffer = null;
}
private void disposeStream ()
{
if (_stream == null)
return;
_stream.Dispose ();
_stream = null;
}
private void disposeTimer ()
{
if (_timer == null)
return;
try {
_timer.Change (Timeout.Infinite, Timeout.Infinite);
}
catch {
}
_timer.Dispose ();
_timer = null;
}
private void init (int timeout)
{
_timeout = timeout;
_context = new HttpListenerContext (this);
_currentLine = new StringBuilder (64);
_inputState = InputState.RequestLine;
_inputStream = null;
_lineState = LineState.None;
_outputStream = null;
_position = 0;
_requestBuffer = new MemoryStream ();
}
private static void onRead (IAsyncResult asyncResult)
{
var conn = (HttpConnection) asyncResult.AsyncState;
var current = conn._attempts;
if (conn._socket == null)
return;
lock (conn._sync) {
if (conn._socket == null)
return;
conn._timer.Change (Timeout.Infinite, Timeout.Infinite);
conn._timeoutCanceled[current] = true;
var nread = 0;
try {
nread = conn._stream.EndRead (asyncResult);
}
catch (Exception) {
// TODO: Logging.
conn.close ();
return;
}
if (nread <= 0) {
conn.close ();
return;
}
conn._requestBuffer.Write (conn._buffer, 0, nread);
var len = (int) conn._requestBuffer.Length;
if (conn.processInput (conn._requestBuffer.GetBuffer (), len)) {
if (!conn._context.HasErrorMessage)
conn._context.Request.FinishInitialization ();
if (conn._context.HasErrorMessage) {
conn._context.SendError ();
return;
}
var url = conn._context.Request.Url;
HttpListener lsnr;
if (conn._listener.TrySearchHttpListener (url, out lsnr)) {
if (!lsnr.AuthenticateContext (conn._context))
return;
if (!lsnr.RegisterContext (conn._context)) {
conn._context.ErrorStatusCode = 503;
conn._context.SendError ();
return;
}
return;
}
conn._context.ErrorStatusCode = 404;
conn._context.SendError ();
return;
}
conn.BeginReadRequest ();
}
}
private static void onTimeout (object state)
{
var conn = (HttpConnection) state;
var current = conn._attempts;
if (conn._socket == null)
return;
lock (conn._sync) {
if (conn._socket == null)
return;
if (conn._timeoutCanceled[current])
return;
conn._context.ErrorStatusCode = 408;
conn._context.SendError ();
}
}
private bool processInput (byte[] data, int length)
{
// This method returns a bool:
// - true Done processing
// - false Need more input
try {
while (true) {
int nread;
var line = readLineFrom (data, _position, length, out nread);
_position += nread;
if (line == null)
break;
if (line.Length == 0) {
if (_inputState == InputState.RequestLine)
continue;
if (_position > _maxInputLength)
_context.ErrorMessage = "Headers too long";
return true;
}
if (_inputState == InputState.RequestLine) {
_context.Request.SetRequestLine (line);
_inputState = InputState.Headers;
}
else {
_context.Request.AddHeader (line);
}
if (_context.HasErrorMessage)
return true;
}
}
catch (Exception ex) {
_context.ErrorMessage = ex.Message;
return true;
}
if (_position >= _maxInputLength) {
_context.ErrorMessage = "Headers too long";
return true;
}
return false;
}
private string readLineFrom (
byte[] buffer, int offset, int length, out int nread
)
{
nread = 0;
for (var i = offset; i < length; i++) {
nread++;
var b = buffer[i];
if (b == 13) {
_lineState = LineState.Cr;
continue;
}
if (b == 10) {
_lineState = LineState.Lf;
break;
}
_currentLine.Append ((char) b);
}
if (_lineState != LineState.Lf)
return null;
var ret = _currentLine.ToString ();
_currentLine.Length = 0;
_lineState = LineState.None;
return ret;
}
#endregion
#region Internal Methods
internal void BeginReadRequest ()
{
_attempts++;
_timeoutCanceled.Add (_attempts, false);
_timer.Change (_timeout, Timeout.Infinite);
try {
_stream.BeginRead (_buffer, 0, _bufferLength, onRead, this);
}
catch (Exception) {
// TODO: Logging.
close ();
}
}
internal void Close (bool force)
{
if (_socket == null)
return;
lock (_sync) {
if (_socket == null)
return;
if (force) {
if (_outputStream != null)
_outputStream.Close (true);
close ();
return;
}
GetResponseStream ().Close (false);
if (_context.Response.CloseConnection) {
close ();
return;
}
if (!_context.Request.FlushInput ()) {
close ();
return;
}
disposeRequestBuffer ();
_context.Unregister ();
_reuses++;
init (15000);
BeginReadRequest ();
}
}
#endregion
#region Public Methods
public void Close ()
{
Close (false);
}
public RequestStream GetRequestStream (long contentLength, bool chunked)
{
lock (_sync) {
if (_socket == null)
return null;
if (_inputStream != null)
return _inputStream;
var buff = _requestBuffer.GetBuffer ();
var len = (int) _requestBuffer.Length;
var cnt = len - _position;
disposeRequestBuffer ();
_inputStream = chunked
? new ChunkedRequestStream (
_stream, buff, _position, cnt, _context
)
: new RequestStream (
_stream, buff, _position, cnt, contentLength
);
return _inputStream;
}
}
public ResponseStream GetResponseStream ()
{
lock (_sync) {
if (_socket == null)
return null;
if (_outputStream != null)
return _outputStream;
var lsnr = _context.Listener;
var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true;
_outputStream = new ResponseStream (_stream, _context.Response, ignore);
return _outputStream;
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69ef566e951884e019553ae4b4a12c26
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,187 @@
#region License
/*
* HttpDigestIdentity.cs
*
* The MIT License
*
* Copyright (c) 2014-2017 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Specialized;
using System.Security.Principal;
namespace WebSocketSharp.Net
{
/// <summary>
/// Holds the username and other parameters from
/// an HTTP Digest authentication attempt.
/// </summary>
public class HttpDigestIdentity : GenericIdentity
{
#region Private Fields
private NameValueCollection _parameters;
#endregion
#region Internal Constructors
internal HttpDigestIdentity (NameValueCollection parameters)
: base (parameters["username"], "Digest")
{
_parameters = parameters;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the algorithm parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the algorithm parameter.
/// </value>
public string Algorithm {
get {
return _parameters["algorithm"];
}
}
/// <summary>
/// Gets the cnonce parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the cnonce parameter.
/// </value>
public string Cnonce {
get {
return _parameters["cnonce"];
}
}
/// <summary>
/// Gets the nc parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the nc parameter.
/// </value>
public string Nc {
get {
return _parameters["nc"];
}
}
/// <summary>
/// Gets the nonce parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the nonce parameter.
/// </value>
public string Nonce {
get {
return _parameters["nonce"];
}
}
/// <summary>
/// Gets the opaque parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the opaque parameter.
/// </value>
public string Opaque {
get {
return _parameters["opaque"];
}
}
/// <summary>
/// Gets the qop parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the qop parameter.
/// </value>
public string Qop {
get {
return _parameters["qop"];
}
}
/// <summary>
/// Gets the realm parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the realm parameter.
/// </value>
public string Realm {
get {
return _parameters["realm"];
}
}
/// <summary>
/// Gets the response parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the response parameter.
/// </value>
public string Response {
get {
return _parameters["response"];
}
}
/// <summary>
/// Gets the uri parameter from a digest authentication attempt.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the uri parameter.
/// </value>
public string Uri {
get {
return _parameters["uri"];
}
}
#endregion
#region Internal Methods
internal bool IsValid (
string password, string realm, string method, string entity
)
{
var copied = new NameValueCollection (_parameters);
copied["password"] = password;
copied["realm"] = realm;
copied["method"] = method;
copied["entity"] = entity;
var expected = AuthenticationResponse.CreateRequestDigest (copied);
return _parameters["response"] == expected;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fdd35886466146b08c7caf3e1a8c50f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,128 @@
#region License
/*
* HttpHeaderInfo.cs
*
* The MIT License
*
* Copyright (c) 2013-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp.Net
{
internal class HttpHeaderInfo
{
#region Private Fields
private string _headerName;
private HttpHeaderType _headerType;
#endregion
#region Internal Constructors
internal HttpHeaderInfo (string headerName, HttpHeaderType headerType)
{
_headerName = headerName;
_headerType = headerType;
}
#endregion
#region Internal Properties
internal bool IsMultiValueInRequest {
get {
var headerType = _headerType & HttpHeaderType.MultiValueInRequest;
return headerType == HttpHeaderType.MultiValueInRequest;
}
}
internal bool IsMultiValueInResponse {
get {
var headerType = _headerType & HttpHeaderType.MultiValueInResponse;
return headerType == HttpHeaderType.MultiValueInResponse;
}
}
#endregion
#region Public Properties
public string HeaderName {
get {
return _headerName;
}
}
public HttpHeaderType HeaderType {
get {
return _headerType;
}
}
public bool IsRequest {
get {
var headerType = _headerType & HttpHeaderType.Request;
return headerType == HttpHeaderType.Request;
}
}
public bool IsResponse {
get {
var headerType = _headerType & HttpHeaderType.Response;
return headerType == HttpHeaderType.Response;
}
}
#endregion
#region Public Methods
public bool IsMultiValue (bool response)
{
var headerType = _headerType & HttpHeaderType.MultiValue;
if (headerType != HttpHeaderType.MultiValue)
return response ? IsMultiValueInResponse : IsMultiValueInRequest;
return response ? IsResponse : IsRequest;
}
public bool IsRestricted (bool response)
{
var headerType = _headerType & HttpHeaderType.Restricted;
if (headerType != HttpHeaderType.Restricted)
return false;
return response ? IsResponse : IsRequest;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ba80abff4de434052a834b7b274396f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,44 @@
#region License
/*
* HttpHeaderType.cs
*
* The MIT License
*
* Copyright (c) 2013-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp.Net
{
[Flags]
internal enum HttpHeaderType
{
Unspecified = 0,
Request = 1,
Response = 1 << 1,
Restricted = 1 << 2,
MultiValue = 1 << 3,
MultiValueInRequest = 1 << 4,
MultiValueInResponse = 1 << 5
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb58e2a936283411aa9669237e42a463
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,973 @@
#region License
/*
* HttpListener.cs
*
* This code is derived from HttpListener.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
#region Contributors
/*
* Contributors:
* - Liryna <liryna.stark@gmail.com>
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Threading;
// TODO: Logging.
namespace WebSocketSharp.Net
{
/// <summary>
/// Provides a simple, programmatically controlled HTTP listener.
/// </summary>
public sealed class HttpListener : IDisposable
{
#region Private Fields
private AuthenticationSchemes _authSchemes;
private Func<HttpListenerRequest, AuthenticationSchemes> _authSchemeSelector;
private string _certFolderPath;
private Queue<HttpListenerContext> _contextQueue;
private LinkedList<HttpListenerContext> _contextRegistry;
private object _contextRegistrySync;
private static readonly string _defaultRealm;
private bool _disposed;
private bool _ignoreWriteExceptions;
private volatile bool _listening;
private Logger _log;
private string _objectName;
private HttpListenerPrefixCollection _prefixes;
private string _realm;
private bool _reuseAddress;
private ServerSslConfiguration _sslConfig;
private Func<IIdentity, NetworkCredential> _userCredFinder;
private Queue<HttpListenerAsyncResult> _waitQueue;
#endregion
#region Static Constructor
static HttpListener ()
{
_defaultRealm = "SECRET AREA";
}
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="HttpListener"/> class.
/// </summary>
public HttpListener ()
{
_authSchemes = AuthenticationSchemes.Anonymous;
_contextQueue = new Queue<HttpListenerContext> ();
_contextRegistry = new LinkedList<HttpListenerContext> ();
_contextRegistrySync = ((ICollection) _contextRegistry).SyncRoot;
_log = new Logger ();
_objectName = GetType ().ToString ();
_prefixes = new HttpListenerPrefixCollection (this);
_waitQueue = new Queue<HttpListenerAsyncResult> ();
}
#endregion
#region Internal Properties
internal bool ReuseAddress {
get {
return _reuseAddress;
}
set {
_reuseAddress = value;
}
}
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the scheme used to authenticate the clients.
/// </summary>
/// <value>
/// <para>
/// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/>
/// enum values.
/// </para>
/// <para>
/// It represents the scheme used to authenticate the clients.
/// </para>
/// <para>
/// The default value is
/// <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
/// </para>
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public AuthenticationSchemes AuthenticationSchemes {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _authSchemes;
}
set {
if (_disposed)
throw new ObjectDisposedException (_objectName);
_authSchemes = value;
}
}
/// <summary>
/// Gets or sets the delegate called to select the scheme used to
/// authenticate the clients.
/// </summary>
/// <remarks>
/// <para>
/// If this property is set, the listener uses the authentication
/// scheme selected by the delegate for each request.
/// </para>
/// <para>
/// Or if this property is not set, the listener uses the value of
/// the <see cref="HttpListener.AuthenticationSchemes"/> property
/// as the authentication scheme for all requests.
/// </para>
/// </remarks>
/// <value>
/// <para>
/// A <c>Func&lt;<see cref="HttpListenerRequest"/>,
/// <see cref="AuthenticationSchemes"/>&gt;</c> delegate or
/// <see langword="null"/> if not needed.
/// </para>
/// <para>
/// The delegate references the method used to select
/// an authentication scheme.
/// </para>
/// <para>
/// The default value is <see langword="null"/>.
/// </para>
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public Func<HttpListenerRequest, AuthenticationSchemes> AuthenticationSchemeSelector {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _authSchemeSelector;
}
set {
if (_disposed)
throw new ObjectDisposedException (_objectName);
_authSchemeSelector = value;
}
}
/// <summary>
/// Gets or sets the path to the folder in which stores the certificate
/// files used to authenticate the server on the secure connection.
/// </summary>
/// <remarks>
/// <para>
/// This property represents the path to the folder in which stores
/// the certificate files associated with each port number of added
/// URI prefixes.
/// </para>
/// <para>
/// A set of the certificate files is a pair of &lt;port number&gt;.cer
/// (DER) and &lt;port number&gt;.key (DER, RSA Private Key).
/// </para>
/// <para>
/// If this property is <see langword="null"/> or an empty string,
/// the result of <c>System.Environment.GetFolderPath (<see
/// cref="Environment.SpecialFolder.ApplicationData"/>)</c>
/// is used as the default path.
/// </para>
/// </remarks>
/// <value>
/// <para>
/// A <see cref="string"/> that represents the path to the folder
/// in which stores the certificate files.
/// </para>
/// <para>
/// The default value is <see langword="null"/>.
/// </para>
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public string CertificateFolderPath {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _certFolderPath;
}
set {
if (_disposed)
throw new ObjectDisposedException (_objectName);
_certFolderPath = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether the listener returns
/// exceptions that occur when sending the response to the client.
/// </summary>
/// <value>
/// <para>
/// <c>true</c> if the listener should not return those exceptions;
/// otherwise, <c>false</c>.
/// </para>
/// <para>
/// The default value is <c>false</c>.
/// </para>
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public bool IgnoreWriteExceptions {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _ignoreWriteExceptions;
}
set {
if (_disposed)
throw new ObjectDisposedException (_objectName);
_ignoreWriteExceptions = value;
}
}
/// <summary>
/// Gets a value indicating whether the listener has been started.
/// </summary>
/// <value>
/// <c>true</c> if the listener has been started; otherwise, <c>false</c>.
/// </value>
public bool IsListening {
get {
return _listening;
}
}
/// <summary>
/// Gets a value indicating whether the listener can be used with
/// the current operating system.
/// </summary>
/// <value>
/// <c>true</c>.
/// </value>
public static bool IsSupported {
get {
return true;
}
}
/// <summary>
/// Gets the logging functions.
/// </summary>
/// <remarks>
/// <para>
/// The default logging level is <see cref="LogLevel.Error"/>.
/// </para>
/// <para>
/// If you would like to change it, you should set the <c>Log.Level</c>
/// property to any of the <see cref="LogLevel"/> enum values.
/// </para>
/// </remarks>
/// <value>
/// A <see cref="Logger"/> that provides the logging functions.
/// </value>
public Logger Log {
get {
return _log;
}
}
/// <summary>
/// Gets the URI prefixes handled by the listener.
/// </summary>
/// <value>
/// A <see cref="HttpListenerPrefixCollection"/> that contains the URI
/// prefixes.
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public HttpListenerPrefixCollection Prefixes {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _prefixes;
}
}
/// <summary>
/// Gets or sets the name of the realm associated with the listener.
/// </summary>
/// <remarks>
/// If this property is <see langword="null"/> or an empty string,
/// "SECRET AREA" will be used as the name of the realm.
/// </remarks>
/// <value>
/// <para>
/// A <see cref="string"/> that represents the name of the realm.
/// </para>
/// <para>
/// The default value is <see langword="null"/>.
/// </para>
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public string Realm {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _realm;
}
set {
if (_disposed)
throw new ObjectDisposedException (_objectName);
_realm = value;
}
}
/// <summary>
/// Gets the SSL configuration used to authenticate the server and
/// optionally the client for secure connection.
/// </summary>
/// <value>
/// A <see cref="ServerSslConfiguration"/> that represents the SSL
/// configuration for secure connection.
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public ServerSslConfiguration SslConfiguration {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
if (_sslConfig == null)
_sslConfig = new ServerSslConfiguration ();
return _sslConfig;
}
}
/// <summary>
/// Gets or sets a value indicating whether, when NTLM authentication is used,
/// the authentication information of first request is used to authenticate
/// additional requests on the same connection.
/// </summary>
/// <remarks>
/// This property is not currently supported and always throws
/// a <see cref="NotSupportedException"/>.
/// </remarks>
/// <value>
/// <c>true</c> if the authentication information of first request is used;
/// otherwise, <c>false</c>.
/// </value>
/// <exception cref="NotSupportedException">
/// Any use of this property.
/// </exception>
public bool UnsafeConnectionNtlmAuthentication {
get {
throw new NotSupportedException ();
}
set {
throw new NotSupportedException ();
}
}
/// <summary>
/// Gets or sets the delegate called to find the credentials for
/// an identity used to authenticate a client.
/// </summary>
/// <value>
/// <para>
/// A <c>Func&lt;<see cref="IIdentity"/>,
/// <see cref="NetworkCredential"/>&gt;</c> delegate or
/// <see langword="null"/> if not needed.
/// </para>
/// <para>
/// It references the method used to find the credentials.
/// </para>
/// <para>
/// The default value is <see langword="null"/>.
/// </para>
/// </value>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
get {
if (_disposed)
throw new ObjectDisposedException (_objectName);
return _userCredFinder;
}
set {
if (_disposed)
throw new ObjectDisposedException (_objectName);
_userCredFinder = value;
}
}
#endregion
#region Private Methods
private HttpListenerAsyncResult beginGetContext (
AsyncCallback callback, object state
)
{
lock (_contextRegistrySync) {
if (!_listening) {
var msg = _disposed
? "The listener is closed."
: "The listener is stopped.";
throw new HttpListenerException (995, msg);
}
var ares = new HttpListenerAsyncResult (callback, state);
if (_contextQueue.Count == 0) {
_waitQueue.Enqueue (ares);
}
else {
var ctx = _contextQueue.Dequeue ();
ares.Complete (ctx, true);
}
return ares;
}
}
private void cleanupContextQueue (bool force)
{
if (_contextQueue.Count == 0)
return;
if (force) {
_contextQueue.Clear ();
return;
}
var ctxs = _contextQueue.ToArray ();
_contextQueue.Clear ();
foreach (var ctx in ctxs) {
ctx.ErrorStatusCode = 503;
ctx.SendError ();
}
}
private void cleanupContextRegistry ()
{
var cnt = _contextRegistry.Count;
if (cnt == 0)
return;
var ctxs = new HttpListenerContext[cnt];
_contextRegistry.CopyTo (ctxs, 0);
_contextRegistry.Clear ();
foreach (var ctx in ctxs)
ctx.Connection.Close (true);
}
private void cleanupWaitQueue (string message)
{
if (_waitQueue.Count == 0)
return;
var aress = _waitQueue.ToArray ();
_waitQueue.Clear ();
foreach (var ares in aress) {
var ex = new HttpListenerException (995, message);
ares.Complete (ex);
}
}
private void close (bool force)
{
if (!_listening) {
_disposed = true;
return;
}
_listening = false;
cleanupContextQueue (force);
cleanupContextRegistry ();
var msg = "The listener is closed.";
cleanupWaitQueue (msg);
EndPointManager.RemoveListener (this);
_disposed = true;
}
private string getRealm ()
{
var realm = _realm;
return realm != null && realm.Length > 0 ? realm : _defaultRealm;
}
private AuthenticationSchemes selectAuthenticationScheme (
HttpListenerRequest request
)
{
var selector = _authSchemeSelector;
if (selector == null)
return _authSchemes;
try {
return selector (request);
}
catch {
return AuthenticationSchemes.None;
}
}
#endregion
#region Internal Methods
internal bool AuthenticateContext (HttpListenerContext context)
{
var req = context.Request;
var schm = selectAuthenticationScheme (req);
if (schm == AuthenticationSchemes.Anonymous)
return true;
if (schm == AuthenticationSchemes.None) {
context.ErrorStatusCode = 403;
context.ErrorMessage = "Authentication not allowed";
context.SendError ();
return false;
}
var realm = getRealm ();
var user = HttpUtility.CreateUser (
req.Headers["Authorization"],
schm,
realm,
req.HttpMethod,
_userCredFinder
);
var authenticated = user != null && user.Identity.IsAuthenticated;
if (!authenticated) {
context.SendAuthenticationChallenge (schm, realm);
return false;
}
context.User = user;
return true;
}
internal void CheckDisposed ()
{
if (_disposed)
throw new ObjectDisposedException (_objectName);
}
internal bool RegisterContext (HttpListenerContext context)
{
if (!_listening)
return false;
lock (_contextRegistrySync) {
if (!_listening)
return false;
context.Listener = this;
_contextRegistry.AddLast (context);
if (_waitQueue.Count == 0) {
_contextQueue.Enqueue (context);
}
else {
var ares = _waitQueue.Dequeue ();
ares.Complete (context, false);
}
return true;
}
}
internal void UnregisterContext (HttpListenerContext context)
{
lock (_contextRegistrySync)
_contextRegistry.Remove (context);
}
#endregion
#region Public Methods
/// <summary>
/// Shuts down the listener immediately.
/// </summary>
public void Abort ()
{
if (_disposed)
return;
lock (_contextRegistrySync) {
if (_disposed)
return;
close (true);
}
}
/// <summary>
/// Begins getting an incoming request asynchronously.
/// </summary>
/// <remarks>
/// <para>
/// This asynchronous operation must be completed by calling
/// the EndGetContext method.
/// </para>
/// <para>
/// Typically, the EndGetContext method is called by
/// <paramref name="callback"/>.
/// </para>
/// </remarks>
/// <returns>
/// An <see cref="IAsyncResult"/> that represents the status of
/// the asynchronous operation.
/// </returns>
/// <param name="callback">
/// An <see cref="AsyncCallback"/> delegate that references the method to
/// invoke when the asynchronous operation completes.
/// </param>
/// <param name="state">
/// An <see cref="object"/> that represents a user defined object to
/// pass to <paramref name="callback"/>.
/// </param>
/// <exception cref="InvalidOperationException">
/// <para>
/// This listener has no URI prefix on which listens.
/// </para>
/// <para>
/// -or-
/// </para>
/// <para>
/// This listener has not been started or is currently stopped.
/// </para>
/// </exception>
/// <exception cref="HttpListenerException">
/// This method is canceled.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public IAsyncResult BeginGetContext (AsyncCallback callback, object state)
{
if (_disposed)
throw new ObjectDisposedException (_objectName);
if (_prefixes.Count == 0) {
var msg = "The listener has no URI prefix on which listens.";
throw new InvalidOperationException (msg);
}
if (!_listening) {
var msg = "The listener has not been started.";
throw new InvalidOperationException (msg);
}
return beginGetContext (callback, state);
}
/// <summary>
/// Shuts down the listener.
/// </summary>
public void Close ()
{
if (_disposed)
return;
lock (_contextRegistrySync) {
if (_disposed)
return;
close (false);
}
}
/// <summary>
/// Ends an asynchronous operation to get an incoming request.
/// </summary>
/// <remarks>
/// This method completes an asynchronous operation started by calling
/// the BeginGetContext method.
/// </remarks>
/// <returns>
/// A <see cref="HttpListenerContext"/> that represents a request.
/// </returns>
/// <param name="asyncResult">
/// An <see cref="IAsyncResult"/> instance obtained by calling
/// the BeginGetContext method.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="asyncResult"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="asyncResult"/> was not obtained by calling
/// the BeginGetContext method.
/// </exception>
/// <exception cref="InvalidOperationException">
/// This method was already called for <paramref name="asyncResult"/>.
/// </exception>
/// <exception cref="HttpListenerException">
/// This method is canceled.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
{
if (_disposed)
throw new ObjectDisposedException (_objectName);
if (asyncResult == null)
throw new ArgumentNullException ("asyncResult");
var ares = asyncResult as HttpListenerAsyncResult;
if (ares == null) {
var msg = "A wrong IAsyncResult instance.";
throw new ArgumentException (msg, "asyncResult");
}
lock (ares.SyncRoot) {
if (ares.EndCalled) {
var msg = "This IAsyncResult instance cannot be reused.";
throw new InvalidOperationException (msg);
}
ares.EndCalled = true;
}
if (!ares.IsCompleted)
ares.AsyncWaitHandle.WaitOne ();
return ares.Context;
}
/// <summary>
/// Gets an incoming request.
/// </summary>
/// <remarks>
/// This method waits for an incoming request and returns when a request is
/// received.
/// </remarks>
/// <returns>
/// A <see cref="HttpListenerContext"/> that represents a request.
/// </returns>
/// <exception cref="InvalidOperationException">
/// <para>
/// This listener has no URI prefix on which listens.
/// </para>
/// <para>
/// -or-
/// </para>
/// <para>
/// This listener has not been started or is currently stopped.
/// </para>
/// </exception>
/// <exception cref="HttpListenerException">
/// This method is canceled.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public HttpListenerContext GetContext ()
{
if (_disposed)
throw new ObjectDisposedException (_objectName);
if (_prefixes.Count == 0) {
var msg = "The listener has no URI prefix on which listens.";
throw new InvalidOperationException (msg);
}
if (!_listening) {
var msg = "The listener has not been started.";
throw new InvalidOperationException (msg);
}
var ares = beginGetContext (null, null);
ares.EndCalled = true;
if (!ares.IsCompleted)
ares.AsyncWaitHandle.WaitOne ();
return ares.Context;
}
/// <summary>
/// Starts receiving incoming requests.
/// </summary>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public void Start ()
{
if (_disposed)
throw new ObjectDisposedException (_objectName);
lock (_contextRegistrySync) {
if (_disposed)
throw new ObjectDisposedException (_objectName);
if (_listening)
return;
EndPointManager.AddListener (this);
_listening = true;
}
}
/// <summary>
/// Stops receiving incoming requests.
/// </summary>
/// <exception cref="ObjectDisposedException">
/// This listener has been closed.
/// </exception>
public void Stop ()
{
if (_disposed)
throw new ObjectDisposedException (_objectName);
lock (_contextRegistrySync) {
if (!_listening)
return;
_listening = false;
cleanupContextQueue (false);
cleanupContextRegistry ();
var msg = "The listener is stopped.";
cleanupWaitQueue (msg);
EndPointManager.RemoveListener (this);
}
}
#endregion
#region Explicit Interface Implementations
/// <summary>
/// Releases all resources used by the listener.
/// </summary>
void IDisposable.Dispose ()
{
if (_disposed)
return;
lock (_contextRegistrySync) {
if (_disposed)
return;
close (true);
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 965e8e05ed542436ab4e04de0d7ae923
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,193 @@
#region License
/*
* HttpListenerAsyncResult.cs
*
* This code is derived from ListenerAsyncResult.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com)
* Copyright (c) 2012-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
*/
#endregion
#region Contributors
/*
* Contributors:
* - Nicholas Devenish
*/
#endregion
using System;
using System.Threading;
namespace WebSocketSharp.Net
{
internal class HttpListenerAsyncResult : IAsyncResult
{
#region Private Fields
private AsyncCallback _callback;
private bool _completed;
private bool _completedSynchronously;
private HttpListenerContext _context;
private bool _endCalled;
private Exception _exception;
private object _state;
private object _sync;
private ManualResetEvent _waitHandle;
#endregion
#region Internal Constructors
internal HttpListenerAsyncResult (AsyncCallback callback, object state)
{
_callback = callback;
_state = state;
_sync = new object ();
}
#endregion
#region Internal Properties
internal HttpListenerContext Context
{
get {
if (_exception != null)
throw _exception;
return _context;
}
}
internal bool EndCalled {
get {
return _endCalled;
}
set {
_endCalled = value;
}
}
internal object SyncRoot {
get {
return _sync;
}
}
#endregion
#region Public Properties
public object AsyncState {
get {
return _state;
}
}
public WaitHandle AsyncWaitHandle {
get {
lock (_sync) {
if (_waitHandle == null)
_waitHandle = new ManualResetEvent (_completed);
return _waitHandle;
}
}
}
public bool CompletedSynchronously {
get {
return _completedSynchronously;
}
}
public bool IsCompleted {
get {
lock (_sync)
return _completed;
}
}
#endregion
#region Private Methods
private void complete ()
{
lock (_sync) {
_completed = true;
if (_waitHandle != null)
_waitHandle.Set ();
}
if (_callback == null)
return;
ThreadPool.QueueUserWorkItem (
state => {
try {
_callback (this);
}
catch {
}
},
null
);
}
#endregion
#region Internal Methods
internal void Complete (Exception exception)
{
_exception = exception;
complete ();
}
internal void Complete (
HttpListenerContext context, bool completedSynchronously
)
{
_context = context;
_completedSynchronously = completedSynchronously;
complete ();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae58f14baea2e45aead7076446b071e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,311 @@
#region License
/*
* HttpListenerContext.cs
*
* This code is derived from HttpListenerContext.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
using System;
using System.Security.Principal;
using System.Text;
using WebSocketSharp.Net.WebSockets;
namespace WebSocketSharp.Net
{
/// <summary>
/// Provides the access to the HTTP request and response objects used by
/// the <see cref="HttpListener"/> class.
/// </summary>
/// <remarks>
/// This class cannot be inherited.
/// </remarks>
public sealed class HttpListenerContext
{
#region Private Fields
private HttpConnection _connection;
private string _errorMessage;
private int _errorStatusCode;
private HttpListener _listener;
private HttpListenerRequest _request;
private HttpListenerResponse _response;
private IPrincipal _user;
private HttpListenerWebSocketContext _websocketContext;
#endregion
#region Internal Constructors
internal HttpListenerContext (HttpConnection connection)
{
_connection = connection;
_errorStatusCode = 400;
_request = new HttpListenerRequest (this);
_response = new HttpListenerResponse (this);
}
#endregion
#region Internal Properties
internal HttpConnection Connection {
get {
return _connection;
}
}
internal string ErrorMessage {
get {
return _errorMessage;
}
set {
_errorMessage = value;
}
}
internal int ErrorStatusCode {
get {
return _errorStatusCode;
}
set {
_errorStatusCode = value;
}
}
internal bool HasErrorMessage {
get {
return _errorMessage != null;
}
}
internal HttpListener Listener {
get {
return _listener;
}
set {
_listener = value;
}
}
#endregion
#region Public Properties
/// <summary>
/// Gets the HTTP request object that represents a client request.
/// </summary>
/// <value>
/// A <see cref="HttpListenerRequest"/> that represents the client request.
/// </value>
public HttpListenerRequest Request {
get {
return _request;
}
}
/// <summary>
/// Gets the HTTP response object used to send a response to the client.
/// </summary>
/// <value>
/// A <see cref="HttpListenerResponse"/> that represents a response to
/// the client request.
/// </value>
public HttpListenerResponse Response {
get {
return _response;
}
}
/// <summary>
/// Gets the client information (identity, authentication, and security
/// roles).
/// </summary>
/// <value>
/// <para>
/// A <see cref="IPrincipal"/> instance or <see langword="null"/>
/// if not authenticated.
/// </para>
/// <para>
/// The instance describes the client.
/// </para>
/// </value>
public IPrincipal User {
get {
return _user;
}
internal set {
_user = value;
}
}
#endregion
#region Private Methods
private static string createErrorContent (
int statusCode, string statusDescription, string message
)
{
return message != null && message.Length > 0
? String.Format (
"<html><body><h1>{0} {1} ({2})</h1></body></html>",
statusCode,
statusDescription,
message
)
: String.Format (
"<html><body><h1>{0} {1}</h1></body></html>",
statusCode,
statusDescription
);
}
#endregion
#region Internal Methods
internal HttpListenerWebSocketContext GetWebSocketContext (string protocol)
{
_websocketContext = new HttpListenerWebSocketContext (this, protocol);
return _websocketContext;
}
internal void SendAuthenticationChallenge (
AuthenticationSchemes scheme, string realm
)
{
var chal = new AuthenticationChallenge (scheme, realm).ToString ();
_response.StatusCode = 401;
_response.Headers.InternalSet ("WWW-Authenticate", chal, true);
_response.Close ();
}
internal void SendError ()
{
try {
_response.StatusCode = _errorStatusCode;
_response.ContentType = "text/html";
var content = createErrorContent (
_errorStatusCode,
_response.StatusDescription,
_errorMessage
);
var enc = Encoding.UTF8;
var entity = enc.GetBytes (content);
_response.ContentEncoding = enc;
_response.ContentLength64 = entity.LongLength;
_response.Close (entity, true);
}
catch {
_connection.Close (true);
}
}
internal void Unregister ()
{
if (_listener == null)
return;
_listener.UnregisterContext (this);
}
#endregion
#region Public Methods
/// <summary>
/// Accepts a WebSocket handshake request.
/// </summary>
/// <returns>
/// A <see cref="HttpListenerWebSocketContext"/> that represents
/// the WebSocket handshake request.
/// </returns>
/// <param name="protocol">
/// A <see cref="string"/> that specifies the subprotocol supported on
/// the WebSocket connection.
/// </param>
/// <exception cref="ArgumentException">
/// <para>
/// <paramref name="protocol"/> is empty.
/// </para>
/// <para>
/// -or-
/// </para>
/// <para>
/// <paramref name="protocol"/> contains an invalid character.
/// </para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// This method has already been called.
/// </exception>
public HttpListenerWebSocketContext AcceptWebSocket (string protocol)
{
if (_websocketContext != null) {
var msg = "The accepting is already in progress.";
throw new InvalidOperationException (msg);
}
if (protocol != null) {
if (protocol.Length == 0) {
var msg = "An empty string.";
throw new ArgumentException (msg, "protocol");
}
if (!protocol.IsToken ()) {
var msg = "It contains an invalid character.";
throw new ArgumentException (msg, "protocol");
}
}
return GetWebSocketContext (protocol);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d3e4e208a399486a93c779183ee4660
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,137 @@
#region License
/*
* HttpListenerException.cs
*
* This code is derived from HttpListenerException.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
*/
#endregion
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
namespace WebSocketSharp.Net
{
/// <summary>
/// The exception that is thrown when an error occurs processing
/// an HTTP request.
/// </summary>
[Serializable]
public class HttpListenerException : Win32Exception
{
#region Protected Constructors
/// <summary>
/// Initializes a new instance of the <see cref="HttpListenerException"/>
/// class from the specified instances of the <see cref="SerializationInfo"/>
/// and <see cref="StreamingContext"/> classes.
/// </summary>
/// <param name="serializationInfo">
/// A <see cref="SerializationInfo"/> that contains the serialized
/// object data.
/// </param>
/// <param name="streamingContext">
/// A <see cref="StreamingContext"/> that specifies the source for
/// the deserialization.
/// </param>
protected HttpListenerException (
SerializationInfo serializationInfo, StreamingContext streamingContext
)
: base (serializationInfo, streamingContext)
{
}
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="HttpListenerException"/>
/// class.
/// </summary>
public HttpListenerException ()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpListenerException"/>
/// class with the specified error code.
/// </summary>
/// <param name="errorCode">
/// An <see cref="int"/> that specifies the error code.
/// </param>
public HttpListenerException (int errorCode)
: base (errorCode)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpListenerException"/>
/// class with the specified error code and message.
/// </summary>
/// <param name="errorCode">
/// An <see cref="int"/> that specifies the error code.
/// </param>
/// <param name="message">
/// A <see cref="string"/> that specifies the message.
/// </param>
public HttpListenerException (int errorCode, string message)
: base (errorCode, message)
{
}
#endregion
#region Public Properties
/// <summary>
/// Gets the error code that identifies the error that occurred.
/// </summary>
/// <value>
/// <para>
/// An <see cref="int"/> that represents the error code.
/// </para>
/// <para>
/// It is any of the Win32 error codes.
/// </para>
/// </value>
public override int ErrorCode {
get {
return NativeErrorCode;
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f2a0a86a16854eca85e7c70bf612815
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,273 @@
#region License
/*
* HttpListenerPrefix.cs
*
* This code is derived from ListenerPrefix.cs (System.Net) of Mono
* (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2012-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Authors
/*
* Authors:
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
* - Oleg Mihailik <mihailik@gmail.com>
*/
#endregion
using System;
namespace WebSocketSharp.Net
{
internal sealed class HttpListenerPrefix
{
#region Private Fields
private string _host;
private HttpListener _listener;
private string _original;
private string _path;
private string _port;
private string _prefix;
private bool _secure;
#endregion
#region Internal Constructors
/// <summary>
/// Initializes a new instance of the <see cref="HttpListenerPrefix"/> class
/// with the specified URI prefix and HTTP listener.
/// </summary>
/// <remarks>
/// This constructor must be called after calling the CheckPrefix method.
/// </remarks>
/// <param name="uriPrefix">
/// A <see cref="string"/> that specifies the URI prefix.
/// </param>
/// <param name="listener">
/// A <see cref="HttpListener"/> that specifies the HTTP listener.
/// </param>
internal HttpListenerPrefix (string uriPrefix, HttpListener listener)
{
_original = uriPrefix;
_listener = listener;
parse (uriPrefix);
}
#endregion
#region Public Properties
public string Host {
get {
return _host;
}
}
public bool IsSecure {
get {
return _secure;
}
}
public HttpListener Listener {
get {
return _listener;
}
}
public string Original {
get {
return _original;
}
}
public string Path {
get {
return _path;
}
}
public string Port {
get {
return _port;
}
}
#endregion
#region Private Methods
private void parse (string uriPrefix)
{
if (uriPrefix.StartsWith ("https"))
_secure = true;
var len = uriPrefix.Length;
var host = uriPrefix.IndexOf (':') + 3;
var root = uriPrefix.IndexOf ('/', host + 1, len - host - 1);
var colon = uriPrefix.LastIndexOf (':', root - 1, root - host - 1);
if (uriPrefix[root - 1] != ']' && colon > host) {
_host = uriPrefix.Substring (host, colon - host);
_port = uriPrefix.Substring (colon + 1, root - colon - 1);
}
else {
_host = uriPrefix.Substring (host, root - host);
_port = _secure ? "443" : "80";
}
_path = uriPrefix.Substring (root);
_prefix = String.Format (
"{0}://{1}:{2}{3}",
_secure ? "https" : "http",
_host,
_port,
_path
);
}
#endregion
#region Public Methods
public static void CheckPrefix (string uriPrefix)
{
if (uriPrefix == null)
throw new ArgumentNullException ("uriPrefix");
var len = uriPrefix.Length;
if (len == 0) {
var msg = "An empty string.";
throw new ArgumentException (msg, "uriPrefix");
}
var schm = uriPrefix.StartsWith ("http://")
|| uriPrefix.StartsWith ("https://");
if (!schm) {
var msg = "The scheme is not 'http' or 'https'.";
throw new ArgumentException (msg, "uriPrefix");
}
var end = len - 1;
if (uriPrefix[end] != '/') {
var msg = "It ends without '/'.";
throw new ArgumentException (msg, "uriPrefix");
}
var host = uriPrefix.IndexOf (':') + 3;
if (host >= end) {
var msg = "No host is specified.";
throw new ArgumentException (msg, "uriPrefix");
}
if (uriPrefix[host] == ':') {
var msg = "No host is specified.";
throw new ArgumentException (msg, "uriPrefix");
}
var root = uriPrefix.IndexOf ('/', host, len - host);
if (root == host) {
var msg = "No host is specified.";
throw new ArgumentException (msg, "uriPrefix");
}
if (uriPrefix[root - 1] == ':') {
var msg = "No port is specified.";
throw new ArgumentException (msg, "uriPrefix");
}
if (root == end - 1) {
var msg = "No path is specified.";
throw new ArgumentException (msg, "uriPrefix");
}
}
/// <summary>
/// Determines whether the current instance is equal to the specified
/// <see cref="object"/> instance.
/// </summary>
/// <remarks>
/// This method will be required to detect duplicates in any collection.
/// </remarks>
/// <param name="obj">
/// <para>
/// An <see cref="object"/> instance to compare to the current instance.
/// </para>
/// <para>
/// An reference to a <see cref="HttpListenerPrefix"/> instance.
/// </para>
/// </param>
/// <returns>
/// <c>true</c> if the current instance and <paramref name="obj"/> have
/// the same URI prefix; otherwise, <c>false</c>.
/// </returns>
public override bool Equals (object obj)
{
var pref = obj as HttpListenerPrefix;
return pref != null && _prefix.Equals (pref._prefix);
}
/// <summary>
/// Gets the hash code for the current instance.
/// </summary>
/// <remarks>
/// This method will be required to detect duplicates in any collection.
/// </remarks>
/// <returns>
/// An <see cref="int"/> that represents the hash code.
/// </returns>
public override int GetHashCode ()
{
return _prefix.GetHashCode ();
}
public override string ToString ()
{
return _prefix;
}
#endregion
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше