Modified HttpListener.cs
This commit is contained in:
@@ -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.
Reference in New Issue
Block a user