Fix for issue #40, and refactored.

This commit is contained in:
sta 2014-04-29 20:48:58 +09:00
parent 6d987ed025
commit 6772efd75c

View File

@ -65,19 +65,19 @@ namespace WebSocketSharp.Net
private HttpListenerContext _context; private HttpListenerContext _context;
private bool _contextWasBound; private bool _contextWasBound;
private StringBuilder _currentLine; private StringBuilder _currentLine;
private EndPointListener _epListener;
private InputState _inputState; private InputState _inputState;
private RequestStream _inputStream; private RequestStream _inputStream;
private HttpListener _lastListener; private HttpListener _lastListener;
private LineState _lineState; private LineState _lineState;
private EndPointListener _listener;
private ResponseStream _outputStream; private ResponseStream _outputStream;
private int _position; private int _position;
private ListenerPrefix _prefix; private ListenerPrefix _prefix;
private MemoryStream _requestBuffer; private MemoryStream _requestBuffer;
private int _reuses; private int _reuses;
private bool _secure;
private Socket _socket; private Socket _socket;
private Stream _stream; private Stream _stream;
private object _sync;
private int _timeout; private int _timeout;
private Timer _timer; private Timer _timer;
@ -88,11 +88,10 @@ namespace WebSocketSharp.Net
public HttpConnection (Socket socket, EndPointListener listener) public HttpConnection (Socket socket, EndPointListener listener)
{ {
_socket = socket; _socket = socket;
_epListener = listener; _listener = listener;
_secure = listener.IsSecure;
var netStream = new NetworkStream (socket, false); var netStream = new NetworkStream (socket, false);
if (_secure) { if (listener.IsSecure) {
var sslStream = new SslStream (netStream, false); var sslStream = new SslStream (netStream, false);
sslStream.AuthenticateAsServer (listener.Certificate); sslStream.AuthenticateAsServer (listener.Certificate);
_stream = sslStream; _stream = sslStream;
@ -101,6 +100,7 @@ namespace WebSocketSharp.Net
_stream = netStream; _stream = netStream;
} }
_sync = new object ();
_timeout = 90000; // 90k ms for first request, 15k ms from then on. _timeout = 90000; // 90k ms for first request, 15k ms from then on.
_timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite); _timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite);
@ -119,7 +119,7 @@ namespace WebSocketSharp.Net
public bool IsSecure { public bool IsSecure {
get { get {
return _secure; return _listener.IsSecure;
} }
} }
@ -161,21 +161,53 @@ namespace WebSocketSharp.Net
#region Private Methods #region Private Methods
private void closeSocket () private void close ()
{ {
if (_socket == null) if (_socket == null)
return; return;
lock (_sync) {
if (_socket == null)
return;
disposeTimer ();
disposeRequestBuffer ();
disposeStream ();
closeSocket ();
}
unbind ();
removeConnection ();
}
private void closeSocket ()
{
try { try {
_socket.Close (); _socket.Shutdown (SocketShutdown.Both);
} }
catch { catch {
} }
finally {
_socket.Close ();
_socket = null; _socket = null;
} }
removeConnection (); private void disposeRequestBuffer ()
{
if (_requestBuffer == null)
return;
_requestBuffer.Dispose ();
_requestBuffer = null;
}
private void disposeStream ()
{
if (_stream == null)
return;
_stream.Dispose ();
_stream = null;
} }
private void disposeTimer () private void disposeTimer ()
@ -183,17 +215,14 @@ namespace WebSocketSharp.Net
if (_timer == null) if (_timer == null)
return; return;
var timer = _timer;
_timer = null;
try { try {
timer.Change (Timeout.Infinite, Timeout.Infinite); _timer.Change (Timeout.Infinite, Timeout.Infinite);
} }
catch { catch {
} }
if (timer != null) _timer.Dispose ();
timer.Dispose (); _timer = null;
} }
private void init () private void init ()
@ -212,6 +241,12 @@ namespace WebSocketSharp.Net
private static void onRead (IAsyncResult asyncResult) private static void onRead (IAsyncResult asyncResult)
{ {
var conn = (HttpConnection) asyncResult.AsyncState; var conn = (HttpConnection) asyncResult.AsyncState;
if (conn._socket == null)
return;
lock (conn._sync) {
if (conn._socket == null)
return;
var read = -1; var read = -1;
try { try {
@ -226,23 +261,16 @@ namespace WebSocketSharp.Net
} }
} }
catch { catch {
if (conn._requestBuffer != null && conn._requestBuffer.Length > 0) var requestBuffer = conn._requestBuffer;
if (requestBuffer != null && requestBuffer.Length > 0)
conn.SendError (); conn.SendError ();
if (conn._socket != null) { conn.close ();
conn.disposeTimer ();
conn.closeSocket ();
conn.unbind ();
}
return; return;
} }
if (read <= 0) { if (read <= 0) {
conn.disposeTimer (); conn.close ();
conn.closeSocket ();
conn.unbind ();
return; return;
} }
@ -257,7 +285,7 @@ namespace WebSocketSharp.Net
return; return;
} }
if (!conn._epListener.BindContext (conn._context)) { if (!conn._listener.BindContext (conn._context)) {
conn.SendError ("Invalid host", 400); conn.SendError ("Invalid host", 400);
conn.Close (true); conn.Close (true);
@ -279,13 +307,12 @@ namespace WebSocketSharp.Net
conn._stream.BeginRead (conn._buffer, 0, _bufferSize, onRead, conn); conn._stream.BeginRead (conn._buffer, 0, _bufferSize, onRead, conn);
} }
}
private static void onTimeout (object state) private static void onTimeout (object state)
{ {
var conn = (HttpConnection) state; var conn = (HttpConnection) state;
conn.disposeTimer (); conn.close ();
conn.closeSocket ();
conn.unbind ();
} }
// true -> Done processing. // true -> Done processing.
@ -363,7 +390,7 @@ namespace WebSocketSharp.Net
private void removeConnection () private void removeConnection ()
{ {
if (_lastListener == null) if (_lastListener == null)
_epListener.RemoveConnection (this); _listener.RemoveConnection (this);
else else
_lastListener.RemoveConnection (this); _lastListener.RemoveConnection (this);
} }
@ -371,7 +398,7 @@ namespace WebSocketSharp.Net
private void unbind () private void unbind ()
{ {
if (_contextWasBound) { if (_contextWasBound) {
_epListener.UnbindContext (_context); _listener.UnbindContext (_context);
_contextWasBound = false; _contextWasBound = false;
} }
} }
@ -382,7 +409,13 @@ namespace WebSocketSharp.Net
internal void Close (bool force) internal void Close (bool force)
{ {
if (_socket != null) { if (_socket == null)
return;
lock (_sync) {
if (_socket == null)
return;
if (_outputStream != null) { if (_outputStream != null) {
_outputStream.Close (); _outputStream.Close ();
_outputStream = null; _outputStream = null;
@ -400,6 +433,7 @@ namespace WebSocketSharp.Net
(!_chunked || (_chunked && !res.ForceCloseChunked))) { (!_chunked || (_chunked && !res.ForceCloseChunked))) {
// Don't close. Keep working. // Don't close. Keep working.
_reuses++; _reuses++;
disposeRequestBuffer ();
unbind (); unbind ();
init (); init ();
BeginReadRequest (); BeginReadRequest ();
@ -407,24 +441,7 @@ namespace WebSocketSharp.Net
return; return;
} }
var socket = _socket; close ();
_socket = null;
disposeTimer ();
try {
socket.Shutdown (SocketShutdown.Both);
}
catch {
}
if (socket != null)
socket.Close ();
unbind ();
removeConnection ();
return;
} }
} }
@ -437,17 +454,15 @@ namespace WebSocketSharp.Net
if (_buffer == null) if (_buffer == null)
_buffer = new byte [_bufferSize]; _buffer = new byte [_bufferSize];
try {
if (_reuses == 1) if (_reuses == 1)
_timeout = 15000; _timeout = 15000;
try {
_timer.Change (_timeout, Timeout.Infinite); _timer.Change (_timeout, Timeout.Infinite);
_stream.BeginRead (_buffer, 0, _bufferSize, onRead, this); _stream.BeginRead (_buffer, 0, _bufferSize, onRead, this);
} }
catch { catch {
disposeTimer (); close ();
closeSocket ();
unbind ();
} }
} }
@ -458,11 +473,16 @@ namespace WebSocketSharp.Net
public RequestStream GetRequestStream (bool chunked, long contentlength) public RequestStream GetRequestStream (bool chunked, long contentlength)
{ {
if (_inputStream == null) { if (_inputStream != null || _socket == null)
return _inputStream;
lock (_sync) {
if (_socket == null)
return _inputStream;
var buffer = _requestBuffer.GetBuffer (); var buffer = _requestBuffer.GetBuffer ();
var length = buffer.Length; var length = buffer.Length;
disposeRequestBuffer ();
_requestBuffer = null;
if (chunked) { if (chunked) {
_chunked = true; _chunked = true;
_context.Response.SendChunked = true; _context.Response.SendChunked = true;
@ -473,22 +493,29 @@ namespace WebSocketSharp.Net
_inputStream = new RequestStream ( _inputStream = new RequestStream (
_stream, buffer, _position, length - _position, contentlength); _stream, buffer, _position, length - _position, contentlength);
} }
}
return _inputStream; return _inputStream;
} }
}
public ResponseStream GetResponseStream () public ResponseStream GetResponseStream ()
{ {
// TODO: Can we get this stream before reading the input? // TODO: Can we get this stream before reading the input?
if (_outputStream == null) {
if (_outputStream != null || _socket == null)
return _outputStream;
lock (_sync) {
if (_socket == null)
return _outputStream;
var listener = _context.Listener; var listener = _context.Listener;
var ignore = listener == null ? true : listener.IgnoreWriteExceptions; var ignore = listener == null ? true : listener.IgnoreWriteExceptions;
_outputStream = new ResponseStream (_stream, _context.Response, ignore); _outputStream = new ResponseStream (_stream, _context.Response, ignore);
}
return _outputStream; return _outputStream;
} }
}
public void SendError () public void SendError ()
{ {
@ -497,23 +524,31 @@ namespace WebSocketSharp.Net
public void SendError (string message, int status) public void SendError (string message, int status)
{ {
if (_socket == null)
return;
lock (_sync) {
if (_socket == null)
return;
try { try {
var res = _context.Response; var res = _context.Response;
res.StatusCode = status; res.StatusCode = status;
res.ContentType = "text/html"; res.ContentType = "text/html";
var description = status.GetStatusDescription (); var desc = status.GetStatusDescription ();
var error = message != null && message.Length > 0 var msg = message != null && message.Length > 0
? String.Format ("<h1>{0} ({1})</h1>", description, message) ? String.Format ("<h1>{0} ({1})</h1>", desc, message)
: String.Format ("<h1>{0}</h1>", description); : String.Format ("<h1>{0}</h1>", desc);
var entity = res.ContentEncoding.GetBytes (error); var entity = res.ContentEncoding.GetBytes (msg);
res.Close (entity, false); res.Close (entity, false);
} }
catch { catch {
// Response was already closed. // Response was already closed.
} }
} }
}
#endregion #endregion
} }