Modified HttpListener.cs

This commit is contained in:
sta 2013-02-19 20:27:27 +09:00
parent f521779ed7
commit a376daedf0
24 changed files with 237 additions and 163 deletions

Binary file not shown.

View File

@ -1,6 +1,6 @@
// //
// HttpConnection.cs // HttpConnection.cs
// Copied from System.Net.HttpConnection // Copied from System.Net.HttpConnection.cs
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // Gonzalo Paniagua Javier (gonzalo@novell.com)
@ -222,10 +222,12 @@ namespace WebSocketSharp.Net {
} catch { } catch {
if (ms != null && ms.Length > 0) if (ms != null && ms.Length > 0)
SendError (); SendError ();
if (sock != null) { if (sock != null) {
CloseSocket (); CloseSocket ();
Unbind (); Unbind ();
} }
return; return;
} }
@ -252,6 +254,7 @@ namespace WebSocketSharp.Net {
Close (true); Close (true);
return; return;
} }
HttpListener listener = context.Listener; HttpListener listener = context.Listener;
if (last_listener != listener) { if (last_listener != listener) {
RemoveConnection (); RemoveConnection ();
@ -263,6 +266,7 @@ namespace WebSocketSharp.Net {
listener.RegisterContext (context); listener.RegisterContext (context);
return; return;
} }
stream.BeginRead (buffer, 0, BufferSize, onread_cb, this); stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
} }
@ -340,6 +344,7 @@ namespace WebSocketSharp.Net {
{ {
if (current_line == null) if (current_line == null)
current_line = new StringBuilder (); current_line = new StringBuilder ();
int last = offset + len; int last = offset + len;
used = 0; used = 0;
for (int i = offset; i < last && line_state != LineState.LF; i++) { for (int i = offset; i < last && line_state != LineState.LF; i++) {
@ -396,15 +401,6 @@ namespace WebSocketSharp.Net {
force_close |= !context.Request.KeepAlive; force_close |= !context.Request.KeepAlive;
if (!force_close) if (!force_close)
force_close = (context.Response.Headers ["connection"] == "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 (!force_close && context.Request.FlushInput ()) {
if (chunked && context.Response.ForceCloseChunked == false) { if (chunked && context.Response.ForceCloseChunked == false) {
@ -433,6 +429,7 @@ namespace WebSocketSharp.Net {
if (s != null) if (s != null)
s.Close (); s.Close ();
} }
Unbind (); Unbind ();
RemoveConnection (); RemoveConnection ();
return; return;
@ -447,9 +444,11 @@ namespace WebSocketSharp.Net {
{ {
if (buffer == null) if (buffer == null)
buffer = new byte [BufferSize]; buffer = new byte [BufferSize];
try { try {
if (reuses == 1) if (reuses == 1)
s_timeout = 15000; s_timeout = 15000;
timer.Change (s_timeout, Timeout.Infinite); timer.Change (s_timeout, Timeout.Infinite);
stream.BeginRead (buffer, 0, BufferSize, onread_cb, this); stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
} catch { } catch {
@ -478,6 +477,7 @@ namespace WebSocketSharp.Net {
i_stream = new RequestStream (stream, buffer, position, length - position, contentlength); i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
} }
} }
return i_stream; return i_stream;
} }
@ -489,6 +489,7 @@ namespace WebSocketSharp.Net {
bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions; bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
o_stream = new ResponseStream (stream, context.Response, ign); o_stream = new ResponseStream (stream, context.Response, ign);
} }
return o_stream; return o_stream;
} }

View File

@ -177,48 +177,67 @@ namespace WebSocketSharp.Net {
#region Private Methods #region Private Methods
void Cleanup (bool close_existing) void Cleanup (bool force)
{ {
lock (((ICollection)registry).SyncRoot) { lock (((ICollection)registry).SyncRoot) {
if (close_existing) { if (!force)
// Need to copy this since closing will call UnregisterContext SendServiceUnavailable ();
ICollection keys = registry.Keys;
var all = new HttpListenerContext [keys.Count]; CleanupContextRegistry ();
keys.CopyTo (all, 0); CleanupConnections ();
registry.Clear (); CleanupWaitQueue ();
for (int i = all.Length - 1; i >= 0; i--) }
all [i].Connection.Close (true); }
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) { wait_queue.Clear ();
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 ();
}
} }
} }
void Close (bool force) void Close (bool force)
{ {
CheckDisposed ();
EndPointManager.RemoveListener (this); EndPointManager.RemoveListener (this);
Cleanup (force); Cleanup (force);
} }
@ -234,6 +253,22 @@ namespace WebSocketSharp.Net {
return context; 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 #endregion
#region Internal Methods #region Internal Methods
@ -322,11 +357,6 @@ namespace WebSocketSharp.Net {
if (disposed) if (disposed)
return; return;
if (!listening) {
disposed = true;
return;
}
Close (true); Close (true);
disposed = true; disposed = true;
} }
@ -383,11 +413,6 @@ namespace WebSocketSharp.Net {
if (disposed) if (disposed)
return; return;
if (!listening) {
disposed = true;
return;
}
Close (false); Close (false);
disposed = true; disposed = true;
} }
@ -487,8 +512,9 @@ namespace WebSocketSharp.Net {
if (!listening) if (!listening)
return; return;
EndPointManager.RemoveListener (this);
listening = false; listening = false;
EndPointManager.RemoveListener (this);
SendServiceUnavailable ();
} }
#endregion #endregion

View File

@ -459,6 +459,7 @@ namespace WebSocketSharp.Net {
IAsyncResult ares = InputStream.BeginRead (bytes, 0, length, null, null); IAsyncResult ares = InputStream.BeginRead (bytes, 0, length, null, null);
if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (100)) if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (100))
return false; return false;
if (InputStream.EndRead (ares) <= 0) if (InputStream.EndRead (ares) <= 0)
return true; return true;
} catch { } catch {

View File

@ -166,7 +166,7 @@ namespace WebSocketSharp.Net {
if (HeadersSent) if (HeadersSent)
throw new InvalidOperationException ("Cannot be changed after headers are sent."); throw new InvalidOperationException ("Cannot be changed after headers are sent.");
keep_alive = value; keep_alive = value;
} }
} }
@ -175,10 +175,11 @@ namespace WebSocketSharp.Net {
get { get {
if (output_stream == null) if (output_stream == null)
output_stream = context.Connection.GetResponseStream (); output_stream = context.Connection.GetResponseStream ();
return output_stream; return output_stream;
} }
} }
public Version ProtocolVersion { public Version ProtocolVersion {
get { return version; } get { return version; }
set { set {
@ -209,7 +210,7 @@ namespace WebSocketSharp.Net {
if (HeadersSent) if (HeadersSent)
throw new InvalidOperationException ("Cannot be changed after headers are sent."); throw new InvalidOperationException ("Cannot be changed after headers are sent.");
location = value; location = value;
} }
} }
@ -222,7 +223,7 @@ namespace WebSocketSharp.Net {
if (HeadersSent) if (HeadersSent)
throw new InvalidOperationException ("Cannot be changed after headers are sent."); throw new InvalidOperationException ("Cannot be changed after headers are sent.");
chunked = value; chunked = value;
} }
} }
@ -261,11 +262,6 @@ namespace WebSocketSharp.Net {
context.Connection.Close (force); context.Connection.Close (force);
} }
void IDisposable.Dispose ()
{
Close (true); // TODO: Abort or Close?
}
bool FindCookie (Cookie cookie) bool FindCookie (Cookie cookie)
{ {
string name = cookie.Name; string name = cookie.Name;
@ -396,6 +392,15 @@ namespace WebSocketSharp.Net {
#endregion #endregion
#region Explicit Interface Implementation
void IDisposable.Dispose ()
{
Close (true); // TODO: Abort or Close?
}
#endregion
#region Public Methods #region Public Methods
public void Abort () public void Abort ()
@ -413,7 +418,7 @@ namespace WebSocketSharp.Net {
if (name == "") if (name == "")
throw new ArgumentException ("'name' cannot be empty", "name"); throw new ArgumentException ("'name' cannot be empty", "name");
// TODO: check for forbidden headers and invalid characters // TODO: check for forbidden headers and invalid characters
if (value.Length > 65535) if (value.Length > 65535)
throw new ArgumentOutOfRangeException ("value"); throw new ArgumentOutOfRangeException ("value");
@ -425,7 +430,7 @@ namespace WebSocketSharp.Net {
{ {
if (cookie == null) if (cookie == null)
throw new ArgumentNullException ("cookie"); throw new ArgumentNullException ("cookie");
Cookies.Add (cookie); Cookies.Add (cookie);
} }
@ -436,7 +441,7 @@ namespace WebSocketSharp.Net {
if (name == "") if (name == "")
throw new ArgumentException ("'name' cannot be empty", "name"); throw new ArgumentException ("'name' cannot be empty", "name");
if (value.Length > 65535) if (value.Length > 65535)
throw new ArgumentOutOfRangeException ("value"); throw new ArgumentOutOfRangeException ("value");

View File

@ -1,6 +1,6 @@
// //
// ResponseStream.cs // ResponseStream.cs
// Copied from System.Net.ResponseStream // Copied from System.Net.ResponseStream.cs
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // 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 // 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 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? // 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; HttpListenerResponse response;
bool ignore_errors; Stream stream;
bool disposed; bool trailer_sent;
bool trailer_sent;
System.IO.Stream stream; #endregion
#region Constructor
internal ResponseStream (System.IO.Stream stream, HttpListenerResponse response, bool ignore_errors) internal ResponseStream (System.IO.Stream stream, HttpListenerResponse response, bool ignore_errors)
{ {
this.stream = stream;
this.response = response; this.response = response;
this.ignore_errors = ignore_errors; this.ignore_errors = ignore_errors;
this.stream = stream;
} }
#endregion
#region Properties
public override bool CanRead { public override bool CanRead {
get { return false; } get { return false; }
} }
@ -77,6 +92,88 @@ namespace WebSocketSharp.Net {
set { 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 () public override void Close ()
{ {
@ -99,39 +196,52 @@ namespace WebSocketSharp.Net {
InternalWrite (bytes, 0, bytes.Length); InternalWrite (bytes, 0, bytes.Length);
trailer_sent = true; trailer_sent = true;
} }
response.Close (); response.Close ();
} }
} }
MemoryStream GetHeaders (bool closing) public override int EndRead (IAsyncResult ares)
{ {
if (response.HeadersSent) throw new NotSupportedException ();
return null; }
MemoryStream ms = new MemoryStream ();
response.SendHeaders (closing, ms); public override void EndWrite (IAsyncResult ares)
return ms; {
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 void Flush ()
{ {
} }
static byte [] crlf = new byte [] { 13, 10 }; public override int Read ([In,Out] byte[] buffer, int offset, int count)
static byte [] GetChunkSizeBytes (int size, bool final)
{ {
string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : ""); throw new NotSupportedException ();
return Encoding.ASCII.GetBytes (str);
} }
internal void InternalWrite (byte [] buffer, int offset, int count) public override long Seek (long offset, SeekOrigin origin)
{ {
if (ignore_errors) { throw new NotSupportedException ();
try { }
stream.Write (buffer, offset, count);
} catch { } public override void SetLength (long value)
} else { {
stream.Write (buffer, offset, count); throw new NotSupportedException ();
}
} }
public override void Write (byte [] buffer, int offset, int count) public override void Write (byte [] buffer, int offset, int count)
@ -164,80 +274,11 @@ namespace WebSocketSharp.Net {
if (count > 0) if (count > 0)
InternalWrite (buffer, offset, count); InternalWrite (buffer, offset, count);
if (chunked) if (chunked)
InternalWrite (crlf, 0, 2); InternalWrite (crlf, 0, 2);
} }
public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count, #endregion
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 ();
}
} }
} }

Binary file not shown.