Fix for HTTP Basic/Digest Authentication
parent
0a263622f0
commit
537229902f
@ -1,69 +1,67 @@
|
||||
//
|
||||
// AuthenticationSchemes.cs
|
||||
// Copied from System.Net.AuthenticationSchemes.cs
|
||||
//
|
||||
// Author:
|
||||
// Atsushi Enomoto <atsushi@ximian.com>
|
||||
//
|
||||
// Copyright (C) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* AuthenticationSchemes.cs
|
||||
*
|
||||
* This code is derived from System.Net.AuthenticationSchemes.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 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 {
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Atsushi Enomoto <atsushi@ximian.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Contains the values of the schemes for authentication.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationSchemes {
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that no authentication is allowed.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Indicates digest authentication.
|
||||
/// </summary>
|
||||
Digest = 1,
|
||||
/// <summary>
|
||||
/// Indicates negotiating with the client to determine the authentication scheme.
|
||||
/// </summary>
|
||||
Negotiate = 2,
|
||||
/// <summary>
|
||||
/// Indicates NTLM authentication.
|
||||
/// </summary>
|
||||
Ntlm = 4,
|
||||
/// <summary>
|
||||
/// Indicates Windows authentication.
|
||||
/// </summary>
|
||||
IntegratedWindowsAuthentication = 6,
|
||||
/// <summary>
|
||||
/// Indicates basic authentication.
|
||||
/// </summary>
|
||||
Basic = 8,
|
||||
/// <summary>
|
||||
/// Indicates anonymous authentication.
|
||||
/// </summary>
|
||||
Anonymous = 0x8000,
|
||||
}
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the values of the schemes for authentication.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationSchemes
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that no authentication is allowed.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Indicates digest authentication.
|
||||
/// </summary>
|
||||
Digest = 1,
|
||||
/// <summary>
|
||||
/// Indicates basic authentication.
|
||||
/// </summary>
|
||||
Basic = 8,
|
||||
/// <summary>
|
||||
/// Indicates anonymous authentication.
|
||||
/// </summary>
|
||||
Anonymous = 0x8000,
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
#region License
|
||||
/*
|
||||
* HttpBasicIdentity.cs
|
||||
*
|
||||
* This code is derived from System.Net.HttpListenerBasicIdentity.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* 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
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the user name and password from an HTTP Basic authentication request.
|
||||
/// </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 an HTTP Basic authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the password.
|
||||
/// </value>
|
||||
public virtual string Password {
|
||||
get {
|
||||
return _password;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,185 @@
|
||||
#region License
|
||||
/*
|
||||
* HttpDigestIdentity.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.Security.Principal;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the user name and other authentication parameters from an HTTP Digest
|
||||
/// authentication request.
|
||||
/// </summary>
|
||||
public class HttpDigestIdentity : GenericIdentity
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private NameValueCollection _params;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal HttpDigestIdentity (NameValueCollection authParams)
|
||||
: base (authParams ["username"], "Digest")
|
||||
{
|
||||
_params = authParams;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the algorithm parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the algorithm parameter.
|
||||
/// </value>
|
||||
public string Algorithm {
|
||||
get {
|
||||
return _params ["algorithm"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cnonce parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the cnonce parameter.
|
||||
/// </value>
|
||||
public string Cnonce {
|
||||
get {
|
||||
return _params ["cnonce"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nc parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the nc parameter.
|
||||
/// </value>
|
||||
public string Nc {
|
||||
get {
|
||||
return _params ["nc"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nonce parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the nonce parameter.
|
||||
/// </value>
|
||||
public string Nonce {
|
||||
get {
|
||||
return _params ["nonce"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opaque parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the opaque parameter.
|
||||
/// </value>
|
||||
public string Opaque {
|
||||
get {
|
||||
return _params ["opaque"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the qop parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the qop parameter.
|
||||
/// </value>
|
||||
public string Qop {
|
||||
get {
|
||||
return _params ["qop"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the realm parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the realm parameter.
|
||||
/// </value>
|
||||
public string Realm {
|
||||
get {
|
||||
return _params ["realm"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the response parameter.
|
||||
/// </value>
|
||||
public string Response {
|
||||
get {
|
||||
return _params ["response"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the uri parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the uri parameter.
|
||||
/// </value>
|
||||
public string Uri {
|
||||
get {
|
||||
return _params ["uri"];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal bool IsValid (
|
||||
string password, string realm, string method, string entity)
|
||||
{
|
||||
var parameters = new NameValueCollection (_params);
|
||||
parameters ["password"] = password;
|
||||
parameters ["realm"] = realm;
|
||||
parameters ["method"] = method;
|
||||
parameters ["entity"] = entity;
|
||||
|
||||
return _params ["response"] == HttpUtility.CreateRequestDigest (parameters);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,222 +1,200 @@
|
||||
//
|
||||
// ListenerAsyncResult.cs
|
||||
// Copied from System.Net.ListenerAsyncResult.cs
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
//
|
||||
// Copyright (c) 2005 Ximian, Inc (http://www.ximian.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* ListenerAsyncResult.cs
|
||||
*
|
||||
* This code is derived from System.Net.ListenerAsyncResult.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com)
|
||||
* Copyright (c) 2012-2013 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.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
class ListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
#region Private Static Field
|
||||
|
||||
static WaitCallback InvokeCB = new WaitCallback (InvokeCallback);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
AsyncCallback cb;
|
||||
bool completed;
|
||||
HttpListenerContext context;
|
||||
Exception exception;
|
||||
ListenerAsyncResult forward;
|
||||
ManualResetEvent handle;
|
||||
object locker;
|
||||
object state;
|
||||
bool synch;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Fields
|
||||
|
||||
internal bool EndCalled;
|
||||
internal bool InGet;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public ListenerAsyncResult (AsyncCallback cb, object state)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.state = state;
|
||||
this.locker = new object();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public object AsyncState {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.AsyncState;
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
public WaitHandle AsyncWaitHandle {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.AsyncWaitHandle;
|
||||
|
||||
lock (locker) {
|
||||
if (handle == null)
|
||||
handle = new ManualResetEvent (completed);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompletedSynchronously {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.CompletedSynchronously;
|
||||
|
||||
return synch;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.IsCompleted;
|
||||
|
||||
lock (locker) {
|
||||
return completed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Method
|
||||
|
||||
static void InvokeCallback (object o)
|
||||
{
|
||||
ListenerAsyncResult ares = (ListenerAsyncResult) o;
|
||||
if (ares.forward != null) {
|
||||
InvokeCallback (ares.forward);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ares.cb (ares);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal void Complete (Exception exc)
|
||||
{
|
||||
if (forward != null) {
|
||||
forward.Complete (exc);
|
||||
return;
|
||||
}
|
||||
|
||||
exception = exc;
|
||||
if (InGet && (exc is ObjectDisposedException))
|
||||
exception = new HttpListenerException (500, "Listener closed");
|
||||
|
||||
lock (locker) {
|
||||
completed = true;
|
||||
if (handle != null)
|
||||
handle.Set ();
|
||||
|
||||
if (cb != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (InvokeCB, this);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Complete (HttpListenerContext context)
|
||||
{
|
||||
Complete (context, false);
|
||||
}
|
||||
|
||||
internal void Complete (HttpListenerContext context, bool synch)
|
||||
{
|
||||
if (forward != null) {
|
||||
forward.Complete (context, synch);
|
||||
return;
|
||||
}
|
||||
|
||||
this.synch = synch;
|
||||
this.context = context;
|
||||
lock (locker) {
|
||||
AuthenticationSchemes schemes = context.Listener.SelectAuthenticationScheme (context);
|
||||
if ((schemes == AuthenticationSchemes.Basic || context.Listener.AuthenticationSchemes == AuthenticationSchemes.Negotiate) && context.Request.Headers ["Authorization"] == null) {
|
||||
context.Response.StatusCode = 401;
|
||||
context.Response.Headers ["WWW-Authenticate"] = schemes + " realm=\"" + context.Listener.Realm + "\"";
|
||||
context.Response.OutputStream.Close ();
|
||||
IAsyncResult ares = context.Listener.BeginGetContext (cb, state);
|
||||
this.forward = (ListenerAsyncResult) ares;
|
||||
lock (forward.locker) {
|
||||
if (handle != null)
|
||||
forward.handle = handle;
|
||||
}
|
||||
|
||||
ListenerAsyncResult next = forward;
|
||||
for (int i = 0; next.forward != null; i++) {
|
||||
if (i > 20)
|
||||
Complete (new HttpListenerException (400, "Too many authentication errors"));
|
||||
|
||||
next = next.forward;
|
||||
}
|
||||
} else {
|
||||
completed = true;
|
||||
if (handle != null)
|
||||
handle.Set ();
|
||||
|
||||
if (cb != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (InvokeCB, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal HttpListenerContext GetContext ()
|
||||
{
|
||||
if (forward != null)
|
||||
return forward.GetContext ();
|
||||
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal class ListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private AsyncCallback _callback;
|
||||
private bool _completed;
|
||||
private HttpListenerContext _context;
|
||||
private Exception _exception;
|
||||
private ManualResetEvent _waitHandle;
|
||||
private object _state;
|
||||
private object _sync;
|
||||
private bool _syncCompleted;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Fields
|
||||
|
||||
internal bool EndCalled;
|
||||
internal bool InGet;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public ListenerAsyncResult (AsyncCallback callback, object state)
|
||||
{
|
||||
_callback = callback;
|
||||
_state = state;
|
||||
_sync = new object ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public object AsyncState {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public WaitHandle AsyncWaitHandle {
|
||||
get {
|
||||
lock (_sync)
|
||||
return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompletedSynchronously {
|
||||
get {
|
||||
return _syncCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted {
|
||||
get {
|
||||
lock (_sync)
|
||||
return _completed;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void invokeCallback (object state)
|
||||
{
|
||||
try {
|
||||
var ares = (ListenerAsyncResult) state;
|
||||
ares._callback (ares);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal void Complete (Exception exception)
|
||||
{
|
||||
_exception = InGet && (exception is ObjectDisposedException)
|
||||
? new HttpListenerException (500, "Listener closed")
|
||||
: exception;
|
||||
|
||||
lock (_sync) {
|
||||
_completed = true;
|
||||
if (_waitHandle != null)
|
||||
_waitHandle.Set ();
|
||||
|
||||
if (_callback != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (invokeCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Complete (HttpListenerContext context)
|
||||
{
|
||||
Complete (context, false);
|
||||
}
|
||||
|
||||
internal void Complete (HttpListenerContext context, bool syncCompleted)
|
||||
{
|
||||
var listener = context.Listener;
|
||||
var scheme = listener.SelectAuthenticationScheme (context);
|
||||
if (scheme == AuthenticationSchemes.None) {
|
||||
context.Response.Close (HttpStatusCode.Forbidden);
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var header = context.Request.Headers ["Authorization"];
|
||||
if (scheme == AuthenticationSchemes.Basic &&
|
||||
(header == null ||
|
||||
!header.StartsWith ("basic", StringComparison.OrdinalIgnoreCase))) {
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateBasicAuthChallenge (listener.Realm));
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scheme == AuthenticationSchemes.Digest &&
|
||||
(header == null ||
|
||||
!header.StartsWith ("digest", StringComparison.OrdinalIgnoreCase))) {
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateDigestAuthChallenge (listener.Realm));
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
_syncCompleted = syncCompleted;
|
||||
|
||||
lock (_sync) {
|
||||
_completed = true;
|
||||
if (_waitHandle != null)
|
||||
_waitHandle.Set ();
|
||||
|
||||
if (_callback != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (invokeCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
internal HttpListenerContext GetContext ()
|
||||
{
|
||||
if (_exception != null)
|
||||
throw _exception;
|
||||
|
||||
return _context;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,179 @@
|
||||
#region License
|
||||
/*
|
||||
* NetworkCredential.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;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the credentials for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
public class NetworkCredential
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _domain;
|
||||
private string _password;
|
||||
private string [] _roles;
|
||||
private string _username;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class
|
||||
/// with the specified user name and password.
|
||||
/// </summary>
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="username"/> is <see langword="null"/> or empty.
|
||||
/// </exception>
|
||||
public NetworkCredential (string username, string password)
|
||||
: this (username, password, null, new string [0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class
|
||||
/// with the specified user name, password, domain, and roles.
|
||||
/// </summary>
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <param name="domain">
|
||||
/// A <see cref="string"/> that represents the name of the user domain
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <param name="roles">
|
||||
/// An array of <see cref="string"/> that contains the role names to which
|
||||
/// the user associated with the credentials belongs if any.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="username"/> is <see langword="null"/> or empty.
|
||||
/// </exception>
|
||||
public NetworkCredential (
|
||||
string username, string password, string domain, params string [] roles)
|
||||
{
|
||||
if (username == null || username.Length == 0)
|
||||
throw new ArgumentException ("Must not be null or empty.", "username");
|
||||
|
||||
_username = username;
|
||||
_password = password;
|
||||
_domain = domain;
|
||||
_roles = roles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the user domain associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the name of the user domain
|
||||
/// associated with the credentials.
|
||||
/// </value>
|
||||
public string Domain {
|
||||
get {
|
||||
return _domain ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_domain = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password for the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </value>
|
||||
public string Password {
|
||||
get {
|
||||
return _password ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_password = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role names to which the user associated with the credentials
|
||||
/// belongs.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An array of <see cref="string"/> that contains the role names to which
|
||||
/// the user associated with the credentials belongs.
|
||||
/// </value>
|
||||
public string [] Roles {
|
||||
get {
|
||||
return _roles;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_roles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </value>
|
||||
public string UserName {
|
||||
get {
|
||||
return _username;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_username = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,253 +1,281 @@
|
||||
//
|
||||
// RequestStream.cs
|
||||
// Copied from System.Net.RequestStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* RequestStream.cs
|
||||
*
|
||||
* This code is derived from System.Net.RequestStream.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
class RequestStream : Stream {
|
||||
|
||||
#region Fields
|
||||
|
||||
byte [] buffer;
|
||||
bool disposed;
|
||||
int length;
|
||||
int offset;
|
||||
long remaining_body;
|
||||
Stream stream;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal RequestStream (Stream stream, byte [] buffer, int offset, int length)
|
||||
: this (stream, buffer, offset, length, -1)
|
||||
{
|
||||
}
|
||||
|
||||
internal RequestStream (Stream stream, byte [] buffer, int offset, int length, long contentlength)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.buffer = buffer;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
this.remaining_body = contentlength;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public override bool CanRead {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get { throw new NotSupportedException (); }
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get { throw new NotSupportedException (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Method
|
||||
|
||||
// Returns 0 if we can keep reading from the base stream,
|
||||
// > 0 if we read something from the buffer.
|
||||
// -1 if we had a content length set and we finished reading that many bytes.
|
||||
int FillFromBuffer (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException ("buffer");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset", "< 0");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException ("count", "< 0");
|
||||
|
||||
int len = buffer.Length;
|
||||
if (offset > len)
|
||||
throw new ArgumentException ("Destination offset is beyond array size.");
|
||||
|
||||
if (offset > len - count)
|
||||
throw new ArgumentException ("Reading would overrun buffer.");
|
||||
|
||||
if (this.remaining_body == 0)
|
||||
return -1;
|
||||
|
||||
if (this.length == 0)
|
||||
return 0;
|
||||
|
||||
int size = Math.Min (this.length, count);
|
||||
if (this.remaining_body > 0)
|
||||
size = (int) Math.Min (size, this.remaining_body);
|
||||
|
||||
if (this.offset > this.buffer.Length - size) {
|
||||
size = Math.Min (size, this.buffer.Length - this.offset);
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
Buffer.BlockCopy (this.buffer, this.offset, buffer, offset, size);
|
||||
this.offset += size;
|
||||
this.length -= size;
|
||||
if (this.remaining_body > 0)
|
||||
remaining_body -= size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer, int offset, int count, AsyncCallback cback, object state)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
int nread = FillFromBuffer (buffer, offset, count);
|
||||
if (nread > 0 || nread == -1) {
|
||||
var ares = new HttpStreamAsyncResult ();
|
||||
ares.Buffer = buffer;
|
||||
ares.Offset = offset;
|
||||
ares.Count = count;
|
||||
ares.Callback = cback;
|
||||
ares.State = state;
|
||||
ares.SyncRead = nread;
|
||||
ares.Complete ();
|
||||
return ares;
|
||||
}
|
||||
|
||||
// Avoid reading past the end of the request to allow
|
||||
// for HTTP pipelining
|
||||
if (remaining_body >= 0 && count > remaining_body)
|
||||
count = (int) Math.Min (Int32.MaxValue, remaining_body);
|
||||
|
||||
return stream.BeginRead (buffer, offset, count, cback, state);
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer, int offset, int count, AsyncCallback cback, object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
if (ares == null)
|
||||
throw new ArgumentNullException ("ares");
|
||||
|
||||
if (ares is HttpStreamAsyncResult) {
|
||||
var ares_ = (HttpStreamAsyncResult) ares;
|
||||
if (!ares.IsCompleted)
|
||||
ares.AsyncWaitHandle.WaitOne ();
|
||||
|
||||
return ares_.SyncRead;
|
||||
}
|
||||
|
||||
// Close on exception?
|
||||
int nread = stream.EndRead (ares);
|
||||
if (remaining_body > 0 && nread > 0)
|
||||
remaining_body -= nread;
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult async_result)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType () .ToString ());
|
||||
|
||||
// Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
|
||||
int nread = FillFromBuffer (buffer, offset, count);
|
||||
if (nread == -1) { // No more bytes available (Content-Length)
|
||||
return 0;
|
||||
} else if (nread > 0) {
|
||||
return nread;
|
||||
}
|
||||
|
||||
nread = stream.Read (buffer, offset, count);
|
||||
if (nread > 0 && remaining_body > 0)
|
||||
remaining_body -= nread;
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal class RequestStream : Stream
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private byte [] _buffer;
|
||||
private bool _disposed;
|
||||
private int _length;
|
||||
private int _offset;
|
||||
private long _remainingBody;
|
||||
private Stream _stream;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal RequestStream (
|
||||
Stream stream, byte [] buffer, int offset, int length)
|
||||
: this (stream, buffer, offset, length, -1)
|
||||
{
|
||||
}
|
||||
|
||||
internal RequestStream (
|
||||
Stream stream, byte [] buffer, int offset, int length, long contentlength)
|
||||
{
|
||||
_stream = stream;
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
_remainingBody = contentlength;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
// Returns 0 if we can keep reading from the base stream,
|
||||
// > 0 if we read something from the buffer.
|
||||
// -1 if we had a content length set and we finished reading that many bytes.
|
||||
private int fillFromBuffer (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException ("buffer");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset", "Less than zero.");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException ("count", "Less than zero.");
|
||||
|
||||
var len = buffer.Length;
|
||||
if (offset > len)
|
||||
throw new ArgumentException ("'offset' is greater than 'buffer' size.");
|
||||
|
||||
if (offset > len - count)
|
||||
throw new ArgumentException ("Reading would overrun 'buffer'.");
|
||||
|
||||
if (_remainingBody == 0)
|
||||
return -1;
|
||||
|
||||
if (_length == 0)
|
||||
return 0;
|
||||
|
||||
var size = _length < count ? _length : count;
|
||||
if (_remainingBody > 0 && _remainingBody < size)
|
||||
size = (int) _remainingBody;
|
||||
|
||||
var remainingBuffer = _buffer.Length - _offset;
|
||||
if (remainingBuffer < size)
|
||||
size = remainingBuffer;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
Buffer.BlockCopy (_buffer, _offset, buffer, offset, size);
|
||||
_offset += size;
|
||||
_length -= size;
|
||||
if (_remainingBody > 0)
|
||||
_remainingBody -= size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
var read = fillFromBuffer (buffer, offset, count);
|
||||
if (read > 0 || read == -1) {
|
||||
var ares = new HttpStreamAsyncResult ();
|
||||
ares.Buffer = buffer;
|
||||
ares.Offset = offset;
|
||||
ares.Count = count;
|
||||
ares.Callback = callback;
|
||||
ares.State = state;
|
||||
ares.SyncRead = read;
|
||||
ares.Complete ();
|
||||
|
||||
return ares;
|
||||
}
|
||||
|
||||
// Avoid reading past the end of the request to allow for HTTP pipelining.
|
||||
if (_remainingBody >= 0 && _remainingBody < count)
|
||||
count = (int) _remainingBody;
|
||||
|
||||
return _stream.BeginRead (buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult asyncResult)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
if (asyncResult == null)
|
||||
throw new ArgumentNullException ("asyncResult");
|
||||
|
||||
if (asyncResult is HttpStreamAsyncResult) {
|
||||
var ares = (HttpStreamAsyncResult) asyncResult;
|
||||
if (!ares.IsCompleted)
|
||||
ares.AsyncWaitHandle.WaitOne ();
|
||||
|
||||
return ares.SyncRead;
|
||||
}
|
||||
|
||||
// Close on exception?
|
||||
var read = _stream.EndRead (asyncResult);
|
||||
if (read > 0 && _remainingBody > 0)
|
||||
_remainingBody -= read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
// Call fillFromBuffer to check for buffer boundaries even when
|
||||
// _remainingBody is 0.
|
||||
var read = fillFromBuffer (buffer, offset, count);
|
||||
if (read == -1) // No more bytes available (Content-Length).
|
||||
return 0;
|
||||
else if (read > 0)
|
||||
return read;
|
||||
|
||||
read = _stream.Read (buffer, offset, count);
|
||||
if (read > 0 && _remainingBody > 0)
|
||||
_remainingBody -= read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,284 +1,322 @@
|
||||
//
|
||||
// ResponseStream.cs
|
||||
// Copied from System.Net.ResponseStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* ResponseStream.cs
|
||||
*
|
||||
* This code is derived from System.Net.ResponseStream.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* 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
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
// FIXME: Does this buffer the response until Close?
|
||||
// Update: we send a single packet for the first non-chunked Write
|
||||
// What happens when we set content-length to X and write X-1 bytes then close?
|
||||
// what if we don't set content-length at all?
|
||||
class ResponseStream : Stream {
|
||||
|
||||
#region Private Static Field
|
||||
|
||||
static byte [] crlf = new byte [] { 13, 10 };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
bool disposed;
|
||||
bool ignore_errors;
|
||||
HttpListenerResponse response;
|
||||
Stream stream;
|
||||
bool trailer_sent;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
internal ResponseStream (System.IO.Stream stream, HttpListenerResponse response, bool ignore_errors)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.response = response;
|
||||
this.ignore_errors = ignore_errors;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public override bool CanRead {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get { throw new NotSupportedException (); }
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get { throw new NotSupportedException (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
static byte [] GetChunkSizeBytes (int size, bool final)
|
||||
{
|
||||
string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
|
||||
return Encoding.ASCII.GetBytes (str);
|
||||
}
|
||||
|
||||
MemoryStream GetHeaders (bool closing)
|
||||
{
|
||||
if (response.HeadersSent)
|
||||
return null;
|
||||
|
||||
MemoryStream ms = new MemoryStream ();
|
||||
response.SendHeaders (closing, ms);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Method
|
||||
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.Write (buffer, offset, count);
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
stream.Write (buffer, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback cback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback cback,
|
||||
object state)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (false);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position;
|
||||
ms.Position = ms.Length;
|
||||
if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
ms.Write (buffer, offset, count);
|
||||
buffer = ms.GetBuffer ();
|
||||
offset = (int) start;
|
||||
count = (int) (ms.Position - start);
|
||||
} else if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
return stream.BeginWrite (buffer, offset, count, cback, state);
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
if (disposed == false) {
|
||||
disposed = true;
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (true);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position;
|
||||
if (chunked && !trailer_sent) {
|
||||
bytes = GetChunkSizeBytes (0, true);
|
||||
ms.Position = ms.Length;
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
|
||||
trailer_sent = true;
|
||||
} else if (chunked && !trailer_sent) {
|
||||
bytes = GetChunkSizeBytes (0, true);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
trailer_sent = true;
|
||||
}
|
||||
|
||||
response.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult ares)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.EndWrite (ares);
|
||||
if (response.SendChunked)
|
||||
stream.Write (crlf, 0, 2);
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
stream.EndWrite (ares);
|
||||
if (response.SendChunked)
|
||||
stream.Write (crlf, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (false);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position; // After the possible preamble for the encoding
|
||||
ms.Position = ms.Length;
|
||||
if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
int new_count = Math.Min (count, 16384 - (int) ms.Position + (int) start);
|
||||
ms.Write (buffer, offset, new_count);
|
||||
count -= new_count;
|
||||
offset += new_count;
|
||||
InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
|
||||
ms.SetLength (0);
|
||||
ms.Capacity = 0; // 'dispose' the buffer in ms.
|
||||
} else if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (crlf, 0, 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
// FIXME: Does this buffer the response until Close?
|
||||
// Update: we send a single packet for the first non-chunked Write
|
||||
// What happens when we set content-length to X and write X-1 bytes then close?
|
||||
// what if we don't set content-length at all?
|
||||
internal class ResponseStream : Stream
|
||||
{
|
||||
#region Private Static Fields
|
||||
|
||||
private static byte [] _crlf = new byte [] { 13, 10 };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool _disposed;
|
||||
private bool _ignoreErrors;
|
||||
private HttpListenerResponse _response;
|
||||
private Stream _stream;
|
||||
private bool _trailerSent;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal ResponseStream (
|
||||
Stream stream, HttpListenerResponse response, bool ignoreErrors)
|
||||
{
|
||||
_stream = stream;
|
||||
_response = response;
|
||||
_ignoreErrors = ignoreErrors;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static byte [] getChunkSizeBytes (int size, bool final)
|
||||
{
|
||||
return Encoding.ASCII.GetBytes (
|
||||
String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : ""));
|
||||
}
|
||||
|
||||
private MemoryStream getHeaders (bool closing)
|
||||
{
|
||||
if (_response.HeadersSent)
|
||||
return null;
|
||||
|
||||
var stream = new MemoryStream ();
|
||||
_response.SendHeaders (closing, stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_ignoreErrors) {
|
||||
try {
|
||||
_stream.Write (buffer, offset, count);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
else {
|
||||
_stream.Write (buffer, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
var stream = getHeaders (false);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
var start = stream.Position;
|
||||
stream.Position = stream.Length;
|
||||
if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
stream.Write (buffer, offset, count);
|
||||
buffer = stream.GetBuffer ();
|
||||
offset = (int) start;
|
||||
count = (int) (stream.Position - start);
|
||||
}
|
||||
else if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
return _stream.BeginWrite (buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
|
||||
var stream = getHeaders (true);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
var start = stream.Position;
|
||||
if (chunked && !_trailerSent) {
|
||||
bytes = getChunkSizeBytes (0, true);
|
||||
stream.Position = stream.Length;
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
InternalWrite (
|
||||
stream.GetBuffer (), (int) start, (int) (stream.Length - start));
|
||||
_trailerSent = true;
|
||||
}
|
||||
else if (chunked && !_trailerSent) {
|
||||
bytes = getChunkSizeBytes (0, true);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
_trailerSent = true;
|
||||
}
|
||||
|
||||
_response.Close ();
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult asyncResult)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
Action<IAsyncResult> endWrite = ares => {
|
||||
_stream.EndWrite (ares);
|
||||
if (_response.SendChunked)
|
||||
_stream.Write (_crlf, 0, 2);
|
||||
};
|
||||
|
||||
if (_ignoreErrors) {
|
||||
try {
|
||||
endWrite (asyncResult);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
else {
|
||||
endWrite (asyncResult);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read (byte [] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
var stream = getHeaders (false);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
// After the possible preamble for the encoding.
|
||||
var start = stream.Position;
|
||||
stream.Position = stream.Length;
|
||||
if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
var newCount = Math.Min (
|
||||
count, 16384 - (int) stream.Position + (int) start);
|
||||
stream.Write (buffer, offset, newCount);
|
||||
count -= newCount;
|
||||
offset += newCount;
|
||||
InternalWrite (
|
||||
stream.GetBuffer (), (int) start, (int) (stream.Length - start));
|
||||
stream.SetLength (0);
|
||||
stream.Capacity = 0; // 'dispose' the buffer in stream.
|
||||
}
|
||||
else if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (_crlf, 0, 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,119 +0,0 @@
|
||||
#region License
|
||||
/*
|
||||
* WsCredential.cs
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2013 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>
|
||||
/// Provides the credentials for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
public class WsCredential {
|
||||
|
||||
#region Private Fields
|
||||
|
||||
string _domain;
|
||||
string _password;
|
||||
string _userName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal WsCredential()
|
||||
{
|
||||
}
|
||||
|
||||
internal WsCredential(string userName, string password)
|
||||
: this(userName, password, null)
|
||||
{
|
||||
}
|
||||
|
||||
internal WsCredential(string userName, string password, string domain)
|
||||
{
|
||||
_userName = userName;
|
||||
_password = password;
|
||||
_domain = domain;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the user domain associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the name of the user domain associated with the credentials.
|
||||
/// Currently, returns the request uri of a WebSocket opening handshake.
|
||||
/// </value>
|
||||
public string Domain {
|
||||
get {
|
||||
return _domain ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_domain = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password for the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the password for the user name associated with the credentials.
|
||||
/// </value>
|
||||
public string Password {
|
||||
get {
|
||||
return _password ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_password = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the user name associated with the credentials.
|
||||
/// </value>
|
||||
public string UserName {
|
||||
get {
|
||||
return _userName ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_userName = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue