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
// 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;
}

View File

@ -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

View File

@ -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 {

View File

@ -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");

View File

@ -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.