Modified HttpListener.cs
This commit is contained in:
parent
f521779ed7
commit
a376daedf0
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
//
|
||||
// HttpConnection.cs
|
||||
// Copied from System.Net.HttpConnection
|
||||
// Copied from System.Net.HttpConnection.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
@ -222,10 +222,12 @@ namespace WebSocketSharp.Net {
|
||||
} catch {
|
||||
if (ms != null && ms.Length > 0)
|
||||
SendError ();
|
||||
|
||||
if (sock != null) {
|
||||
CloseSocket ();
|
||||
Unbind ();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -252,6 +254,7 @@ namespace WebSocketSharp.Net {
|
||||
Close (true);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpListener listener = context.Listener;
|
||||
if (last_listener != listener) {
|
||||
RemoveConnection ();
|
||||
@ -263,6 +266,7 @@ namespace WebSocketSharp.Net {
|
||||
listener.RegisterContext (context);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
|
||||
}
|
||||
|
||||
@ -340,6 +344,7 @@ namespace WebSocketSharp.Net {
|
||||
{
|
||||
if (current_line == null)
|
||||
current_line = new StringBuilder ();
|
||||
|
||||
int last = offset + len;
|
||||
used = 0;
|
||||
for (int i = offset; i < last && line_state != LineState.LF; i++) {
|
||||
@ -396,15 +401,6 @@ namespace WebSocketSharp.Net {
|
||||
force_close |= !context.Request.KeepAlive;
|
||||
if (!force_close)
|
||||
force_close = (context.Response.Headers ["connection"] == "close");
|
||||
/*
|
||||
if (!force_close) {
|
||||
// bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
|
||||
// status_code == 413 || status_code == 414 || status_code == 500 ||
|
||||
// status_code == 503);
|
||||
|
||||
force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
|
||||
}
|
||||
*/
|
||||
|
||||
if (!force_close && context.Request.FlushInput ()) {
|
||||
if (chunked && context.Response.ForceCloseChunked == false) {
|
||||
@ -433,6 +429,7 @@ namespace WebSocketSharp.Net {
|
||||
if (s != null)
|
||||
s.Close ();
|
||||
}
|
||||
|
||||
Unbind ();
|
||||
RemoveConnection ();
|
||||
return;
|
||||
@ -447,9 +444,11 @@ namespace WebSocketSharp.Net {
|
||||
{
|
||||
if (buffer == null)
|
||||
buffer = new byte [BufferSize];
|
||||
|
||||
try {
|
||||
if (reuses == 1)
|
||||
s_timeout = 15000;
|
||||
|
||||
timer.Change (s_timeout, Timeout.Infinite);
|
||||
stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
|
||||
} catch {
|
||||
@ -478,6 +477,7 @@ namespace WebSocketSharp.Net {
|
||||
i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
|
||||
}
|
||||
}
|
||||
|
||||
return i_stream;
|
||||
}
|
||||
|
||||
@ -489,6 +489,7 @@ namespace WebSocketSharp.Net {
|
||||
bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
|
||||
o_stream = new ResponseStream (stream, context.Response, ign);
|
||||
}
|
||||
|
||||
return o_stream;
|
||||
}
|
||||
|
||||
|
@ -177,48 +177,67 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
#region Private Methods
|
||||
|
||||
void Cleanup (bool close_existing)
|
||||
void Cleanup (bool force)
|
||||
{
|
||||
lock (((ICollection)registry).SyncRoot) {
|
||||
if (close_existing) {
|
||||
// Need to copy this since closing will call UnregisterContext
|
||||
ICollection keys = registry.Keys;
|
||||
var all = new HttpListenerContext [keys.Count];
|
||||
keys.CopyTo (all, 0);
|
||||
registry.Clear ();
|
||||
for (int i = all.Length - 1; i >= 0; i--)
|
||||
all [i].Connection.Close (true);
|
||||
if (!force)
|
||||
SendServiceUnavailable ();
|
||||
|
||||
CleanupContextRegistry ();
|
||||
CleanupConnections ();
|
||||
CleanupWaitQueue ();
|
||||
}
|
||||
}
|
||||
|
||||
void CleanupConnections ()
|
||||
{
|
||||
lock (((ICollection)connections).SyncRoot) {
|
||||
if (connections.Count == 0)
|
||||
return;
|
||||
|
||||
// Need to copy this since closing will call RemoveConnection
|
||||
ICollection keys = connections.Keys;
|
||||
var conns = new HttpConnection [keys.Count];
|
||||
keys.CopyTo (conns, 0);
|
||||
connections.Clear ();
|
||||
for (int i = conns.Length - 1; i >= 0; i--)
|
||||
conns [i].Close (true);
|
||||
}
|
||||
}
|
||||
|
||||
void CleanupContextRegistry ()
|
||||
{
|
||||
lock (((ICollection)registry).SyncRoot) {
|
||||
if (registry.Count == 0)
|
||||
return;
|
||||
|
||||
// Need to copy this since closing will call UnregisterContext
|
||||
ICollection keys = registry.Keys;
|
||||
var all = new HttpListenerContext [keys.Count];
|
||||
keys.CopyTo (all, 0);
|
||||
registry.Clear ();
|
||||
for (int i = all.Length - 1; i >= 0; i--)
|
||||
all [i].Connection.Close (true);
|
||||
}
|
||||
}
|
||||
|
||||
void CleanupWaitQueue ()
|
||||
{
|
||||
lock (((ICollection)wait_queue).SyncRoot) {
|
||||
if (wait_queue.Count == 0)
|
||||
return;
|
||||
|
||||
var exc = new ObjectDisposedException (GetType ().ToString ());
|
||||
foreach (var ares in wait_queue) {
|
||||
ares.Complete (exc);
|
||||
}
|
||||
|
||||
lock (((ICollection)connections).SyncRoot) {
|
||||
ICollection keys = connections.Keys;
|
||||
var conns = new HttpConnection [keys.Count];
|
||||
keys.CopyTo (conns, 0);
|
||||
connections.Clear ();
|
||||
for (int i = conns.Length - 1; i >= 0; i--)
|
||||
conns [i].Close (true);
|
||||
}
|
||||
|
||||
lock (((ICollection)ctx_queue).SyncRoot) {
|
||||
var ctxs = ctx_queue.ToArray ();
|
||||
ctx_queue.Clear ();
|
||||
for (int i = ctxs.Length - 1; i >= 0; i--)
|
||||
ctxs [i].Connection.Close (true);
|
||||
}
|
||||
|
||||
lock (((ICollection)wait_queue).SyncRoot) {
|
||||
Exception exc = new ObjectDisposedException ("listener");
|
||||
foreach (ListenerAsyncResult ares in wait_queue) {
|
||||
ares.Complete (exc);
|
||||
}
|
||||
wait_queue.Clear ();
|
||||
}
|
||||
wait_queue.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void Close (bool force)
|
||||
{
|
||||
CheckDisposed ();
|
||||
EndPointManager.RemoveListener (this);
|
||||
Cleanup (force);
|
||||
}
|
||||
@ -234,6 +253,22 @@ namespace WebSocketSharp.Net {
|
||||
return context;
|
||||
}
|
||||
|
||||
void SendServiceUnavailable ()
|
||||
{
|
||||
lock (((ICollection)ctx_queue).SyncRoot) {
|
||||
if (ctx_queue.Count == 0)
|
||||
return;
|
||||
|
||||
var ctxs = ctx_queue.ToArray ();
|
||||
ctx_queue.Clear ();
|
||||
foreach (var ctx in ctxs) {
|
||||
var res = ctx.Response;
|
||||
res.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
|
||||
res.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
@ -322,11 +357,6 @@ namespace WebSocketSharp.Net {
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (!listening) {
|
||||
disposed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Close (true);
|
||||
disposed = true;
|
||||
}
|
||||
@ -383,11 +413,6 @@ namespace WebSocketSharp.Net {
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (!listening) {
|
||||
disposed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Close (false);
|
||||
disposed = true;
|
||||
}
|
||||
@ -487,8 +512,9 @@ namespace WebSocketSharp.Net {
|
||||
if (!listening)
|
||||
return;
|
||||
|
||||
EndPointManager.RemoveListener (this);
|
||||
listening = false;
|
||||
EndPointManager.RemoveListener (this);
|
||||
SendServiceUnavailable ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -459,6 +459,7 @@ namespace WebSocketSharp.Net {
|
||||
IAsyncResult ares = InputStream.BeginRead (bytes, 0, length, null, null);
|
||||
if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (100))
|
||||
return false;
|
||||
|
||||
if (InputStream.EndRead (ares) <= 0)
|
||||
return true;
|
||||
} catch {
|
||||
|
@ -166,7 +166,7 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
if (HeadersSent)
|
||||
throw new InvalidOperationException ("Cannot be changed after headers are sent.");
|
||||
|
||||
|
||||
keep_alive = value;
|
||||
}
|
||||
}
|
||||
@ -175,10 +175,11 @@ namespace WebSocketSharp.Net {
|
||||
get {
|
||||
if (output_stream == null)
|
||||
output_stream = context.Connection.GetResponseStream ();
|
||||
|
||||
return output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Version ProtocolVersion {
|
||||
get { return version; }
|
||||
set {
|
||||
@ -209,7 +210,7 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
if (HeadersSent)
|
||||
throw new InvalidOperationException ("Cannot be changed after headers are sent.");
|
||||
|
||||
|
||||
location = value;
|
||||
}
|
||||
}
|
||||
@ -222,7 +223,7 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
if (HeadersSent)
|
||||
throw new InvalidOperationException ("Cannot be changed after headers are sent.");
|
||||
|
||||
|
||||
chunked = value;
|
||||
}
|
||||
}
|
||||
@ -261,11 +262,6 @@ namespace WebSocketSharp.Net {
|
||||
context.Connection.Close (force);
|
||||
}
|
||||
|
||||
void IDisposable.Dispose ()
|
||||
{
|
||||
Close (true); // TODO: Abort or Close?
|
||||
}
|
||||
|
||||
bool FindCookie (Cookie cookie)
|
||||
{
|
||||
string name = cookie.Name;
|
||||
@ -396,6 +392,15 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
#endregion
|
||||
|
||||
#region Explicit Interface Implementation
|
||||
|
||||
void IDisposable.Dispose ()
|
||||
{
|
||||
Close (true); // TODO: Abort or Close?
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Abort ()
|
||||
@ -413,7 +418,7 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
if (name == "")
|
||||
throw new ArgumentException ("'name' cannot be empty", "name");
|
||||
|
||||
|
||||
// TODO: check for forbidden headers and invalid characters
|
||||
if (value.Length > 65535)
|
||||
throw new ArgumentOutOfRangeException ("value");
|
||||
@ -425,7 +430,7 @@ namespace WebSocketSharp.Net {
|
||||
{
|
||||
if (cookie == null)
|
||||
throw new ArgumentNullException ("cookie");
|
||||
|
||||
|
||||
Cookies.Add (cookie);
|
||||
}
|
||||
|
||||
@ -436,7 +441,7 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
if (name == "")
|
||||
throw new ArgumentException ("'name' cannot be empty", "name");
|
||||
|
||||
|
||||
if (value.Length > 65535)
|
||||
throw new ArgumentOutOfRangeException ("value");
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//
|
||||
// ResponseStream.cs
|
||||
// Copied from System.Net.ResponseStream
|
||||
// Copied from System.Net.ResponseStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
@ -40,22 +40,37 @@ namespace WebSocketSharp.Net {
|
||||
// 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
|
||||
{
|
||||
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;
|
||||
bool ignore_errors;
|
||||
bool disposed;
|
||||
bool trailer_sent;
|
||||
System.IO.Stream stream;
|
||||
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;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public override bool CanRead {
|
||||
get { return false; }
|
||||
}
|
||||
@ -77,6 +92,88 @@ namespace WebSocketSharp.Net {
|
||||
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 ()
|
||||
{
|
||||
@ -99,39 +196,52 @@ namespace WebSocketSharp.Net {
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
trailer_sent = true;
|
||||
}
|
||||
|
||||
response.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
MemoryStream GetHeaders (bool closing)
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
if (response.HeadersSent)
|
||||
return null;
|
||||
MemoryStream ms = new MemoryStream ();
|
||||
response.SendHeaders (closing, ms);
|
||||
return ms;
|
||||
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 ()
|
||||
{
|
||||
}
|
||||
|
||||
static byte [] crlf = new byte [] { 13, 10 };
|
||||
static byte [] GetChunkSizeBytes (int size, bool final)
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
|
||||
return Encoding.ASCII.GetBytes (str);
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.Write (buffer, offset, count);
|
||||
} catch { }
|
||||
} else {
|
||||
stream.Write (buffer, offset, count);
|
||||
}
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
@ -164,80 +274,11 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (crlf, 0, 2);
|
||||
}
|
||||
|
||||
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 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 int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
|
||||
AsyncCallback cback, object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user