Fix for issue #10

master
sta 13 years ago
parent 8aab7bebe0
commit 4424d1d809

Binary file not shown.

@ -73,11 +73,11 @@ namespace Example
ThreadPool.QueueUserWorkItem(notifyMsg);
//using (WebSocket ws = new WebSocket("ws://echo.websocket.org", "echo"))
using (WebSocket ws = new WebSocket("ws://echo.websocket.org", "echo"))
//using (WebSocket ws = new WebSocket("wss://echo.websocket.org", "echo"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Echo"))
using (WebSocket ws = new WebSocket("ws://localhost:4649/Echo?name=nobita"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Echo?name=nobita"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/エコー?name=のび太"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat?name=nobita"))

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.

@ -294,8 +294,8 @@ Please access [http://localhost:4649](http://localhost:4649) to do WebSocket Ech
**websocket-sharp** supports **[RFC 6455]**.
- @**[branch: hybi-00]** supports older draft-ietf-hybi-thewebsocketprotocol-00 (**[hybi-00]**).
- @**[branch: draft75]** supports even more old draft-hixie-thewebsocketprotocol-75 (**[hixie-75]**).
- **[branch: hybi-00]** supports older draft-ietf-hybi-thewebsocketprotocol-00 (**[hybi-00]**).
- **[branch: draft75]** supports even more old draft-hixie-thewebsocketprotocol-75 (**[hixie-75]**).
## Reference ##

@ -104,16 +104,14 @@ namespace WebSocketSharp.Frame {
public ulong Length
{
get
{
get {
return 2 + (ulong)(ExtPayloadLen.Length + MaskingKey.Length) + PayloadLength;
}
}
public ulong PayloadLength
{
get
{
get {
return PayloadData.Length;
}
}
@ -144,6 +142,31 @@ namespace WebSocketSharp.Frame {
PayloadData.Mask(key);
}
private static WsFrame parse(Stream stream, bool unmask)
{
return parse(stream.ReadBytes(2), stream, unmask);
}
private static WsFrame parse(byte[] header, Stream stream, bool unmask)
{
if (header.IsNull() || header.Length != 2)
return null;
try
{
var frame = readHeader(header);
readExtPayloadLen(stream, frame);
readMaskingKey(stream, frame);
readPayloadData(stream, frame, unmask);
return frame;
}
catch
{
return null;
}
}
private static void readExtPayloadLen(Stream stream, WsFrame frame)
{
var length = frame.PayloadLen <= 125
@ -155,16 +178,13 @@ namespace WebSocketSharp.Frame {
var extLength = stream.ReadBytes(length);
if (extLength == null)
throw new IOException();
frame.ExtPayloadLen = extLength;
}
}
private static WsFrame readHeader(Stream stream)
private static WsFrame readHeader(byte[] header)
{
var header = stream.ReadBytes(2);
if (header == null)
return null;
// FIN
Fin fin = (header[0] & 0x80) == 0x80 ? Fin.FINAL : Fin.MORE;
// RSV1
@ -187,7 +207,8 @@ namespace WebSocketSharp.Frame {
Rsv3 = rsv3,
Opcode = opcode,
Masked = masked,
PayloadLen = payloadLen};
PayloadLen = payloadLen
};
}
private static void readMaskingKey(Stream stream, WsFrame frame)
@ -197,6 +218,7 @@ namespace WebSocketSharp.Frame {
var maskingKey = stream.ReadBytes(4);
if (maskingKey == null)
throw new IOException();
frame.MaskingKey = maskingKey;
}
}
@ -284,15 +306,41 @@ namespace WebSocketSharp.Frame {
public static WsFrame Parse(Stream stream, bool unmask)
{
var frame = readHeader(stream);
if (frame == null)
return null;
return parse(stream, unmask);
}
readExtPayloadLen(stream, frame);
readMaskingKey(stream, frame);
readPayloadData(stream, frame, unmask);
public static void ParseAsync(Stream stream, Action<WsFrame> completed)
{
ParseAsync(stream, true, completed);
}
return frame;
public static void ParseAsync(Stream stream, bool unmask, Action<WsFrame> completed)
{
var headerLen = 2;
var header = new byte[headerLen];
AsyncCallback callback = (ar) =>
{
WsFrame frame;
try
{
var readLen = stream.EndRead(ar);
frame = readLen == 2
? parse(header, stream, unmask)
: null;
}
catch
{
frame = null;
}
finally
{
if (!completed.IsNull())
completed(frame);
}
};
stream.BeginRead(header, 0, headerLen, callback, null);
}
public void Print()

@ -55,7 +55,7 @@ namespace WebSocketSharp.Server {
_isStopped = false;
_isSweeping = false;
_sessions = new Dictionary<string, WebSocketService>();
_sweepTimer = new Timer(30 * 1000);
_sweepTimer = new Timer(60 * 1000);
_sweepTimer.Elapsed += (sender, e) =>
{
Sweep();
@ -127,6 +127,56 @@ namespace WebSocketSharp.Server {
#region Private Methods
private void broadcast(byte[] data)
{
lock (_syncRoot)
{
foreach (var service in _sessions.Values)
service.Send(data);
}
}
private void broadcast(string data)
{
lock (_syncRoot)
{
foreach (var service in _sessions.Values)
service.Send(data);
}
}
private void broadcastAsync(byte[] data)
{
var sessions = copySessions();
var services = sessions.Values.GetEnumerator();
Action completed = null;
completed = () =>
{
if (services.MoveNext())
services.Current.SendAsync(data, completed);
};
if (services.MoveNext())
services.Current.SendAsync(data, completed);
}
private void broadcastAsync(string data)
{
var sessions = copySessions();
var services = sessions.Values.GetEnumerator();
Action completed = null;
completed = () =>
{
if (services.MoveNext())
services.Current.SendAsync(data, completed);
};
if (services.MoveNext())
services.Current.SendAsync(data, completed);
}
private Dictionary<string, WebSocketService> copySessions()
{
lock (_syncRoot)
@ -172,20 +222,18 @@ namespace WebSocketSharp.Server {
public void Broadcast(byte[] data)
{
lock (_syncRoot)
{
foreach (var service in _sessions.Values)
service.Send(data);
}
if (_isStopped)
broadcast(data);
else
broadcastAsync(data);
}
public void Broadcast(string data)
{
lock (_syncRoot)
{
foreach (var service in _sessions.Values)
service.Send(data);
}
if (_isStopped)
broadcast(data);
else
broadcastAsync(data);
}
public Dictionary<string, bool> Broadping(string message)

@ -113,6 +113,20 @@ namespace WebSocketSharp.Server {
#endregion
#region Internal Methods
internal void SendAsync(byte[] data, Action completed)
{
_socket.SendAsync(data, completed);
}
internal void SendAsync(string data, Action completed)
{
_socket.SendAsync(data, completed);
}
#endregion
#region Public Methods
public void Bind(WebSocket socket, SessionManager sessions)

@ -610,6 +610,19 @@ namespace WebSocketSharp {
return true;
}
private bool isValidFrame(WsFrame frame)
{
if (frame.IsNull())
{
var msg = "The WebSocket frame can not be read from the network stream.";
close(CloseStatusCode.ABNORMAL, msg);
return false;
}
return true;
}
// As Server
private bool isValidRequest(RequestHandshake request, out string message)
{
@ -704,35 +717,6 @@ namespace WebSocketSharp {
return true;
}
private void message()
{
try
{
onMessage(receive());
}
catch (WsReceivedTooBigMessageException ex)
{
close(CloseStatusCode.TOO_BIG, ex.Message);
}
catch (Exception)
{
close(CloseStatusCode.ABNORMAL, "An exception has occured.");
}
}
private void messageLoopCallback(IAsyncResult ar)
{
Action messageInvoker = (Action)ar.AsyncState;
messageInvoker.EndInvoke(ar);
if (_readyState == WsState.OPEN)
{
messageInvoker.BeginInvoke(messageLoopCallback, messageInvoker);
return;
}
_exitMessageLoop.Set();
}
private void onClose(CloseEventArgs eventArgs)
{
if (!Thread.CurrentThread.IsBackground)
@ -764,7 +748,7 @@ namespace WebSocketSharp {
private void onOpen()
{
_readyState = WsState.OPEN;
startMessageThread();
startMessageLoop();
OnOpen.Emit(this, EventArgs.Empty);
}
@ -799,25 +783,7 @@ namespace WebSocketSharp {
private WsFrame readFrame()
{
var frame = _wsStream.ReadFrame();
if (frame.IsNull())
{
var msg = "The WebSocket frame can not be read from network stream.";
close(CloseStatusCode.ABNORMAL, msg);
}
return frame;
}
private WsFrame readFrameWithTimeout(int millisecondsTimeout)
{
if (!_wsStream.DataAvailable)
{
Thread.Sleep(millisecondsTimeout);
if (!_wsStream.DataAvailable)
return null;
}
return readFrame();
return isValidFrame(frame) ? frame : null;
}
private string[] readHandshake()
@ -825,10 +791,9 @@ namespace WebSocketSharp {
return _wsStream.ReadHandshake();
}
private MessageEventArgs receive()
private MessageEventArgs receive(WsFrame frame)
{
var frame = _isClient ? readFrame() : readFrameWithTimeout(1 * 100);
if (frame.IsNull())
if (!isValidFrame(frame))
return null;
if ((frame.Fin == Fin.FINAL && frame.Opcode == Opcode.CONT) ||
@ -838,10 +803,9 @@ namespace WebSocketSharp {
if (frame.Fin == Fin.MORE)
{// MORE
var merged = receiveFragmented(frame);
if (merged.IsNull())
return null;
return new MessageEventArgs(frame.Opcode, new PayloadData(merged));
return !merged.IsNull()
? new MessageEventArgs(frame.Opcode, new PayloadData(merged))
: null;
}
if (frame.Opcode == Opcode.CLOSE)
@ -1015,6 +979,8 @@ namespace WebSocketSharp {
private void send(Opcode opcode, Stream stream)
{
lock (_forSend)
{
try
{
if (_readyState != WsState.OPEN)
{
@ -1025,13 +991,14 @@ namespace WebSocketSharp {
var length = stream.Length;
if (length <= _fragmentLen)
send(Fin.FINAL, opcode, stream.ReadBytes((int)length));
else
sendFragmented(opcode, stream);
}
catch (Exception ex)
{
var buffer = stream.ReadBytes((int)length);
send(Fin.FINAL, opcode, buffer);
return;
onError(ex.Message);
}
sendFragmented(opcode, stream);
}
}
@ -1049,13 +1016,23 @@ namespace WebSocketSharp {
private void sendAsync(Opcode opcode, Stream stream, Action completed)
{
Action<Opcode, Stream> action = send;
AsyncCallback callback = null;
callback = (ar) =>
AsyncCallback callback = (ar) =>
{
try
{
action.EndInvoke(ar);
stream.Close();
if (!completed.IsNull())
completed();
}
catch (Exception ex)
{
onError(ex.Message);
}
finally
{
stream.Close();
}
};
action.BeginInvoke(opcode, stream, callback, null);
@ -1132,17 +1109,33 @@ namespace WebSocketSharp {
writeHandshake(response);
}
private void startMessageThread()
private void startMessageLoop()
{
_receivePong = new AutoResetEvent(false);
_exitMessageLoop = new AutoResetEvent(false);
Action messageInvoker = () =>
_receivePong = new AutoResetEvent(false);
Action<WsFrame> completed = null;
completed = (frame) =>
{
try
{
onMessage(receive(frame));
if (_readyState == WsState.OPEN)
message();
_wsStream.ReadFrameAsync(completed);
else
_exitMessageLoop.Set();
}
catch (WsReceivedTooBigMessageException ex)
{
close(CloseStatusCode.TOO_BIG, ex.Message);
}
catch (Exception)
{
close(CloseStatusCode.ABNORMAL, "An exception has occured.");
}
};
messageInvoker.BeginInvoke(messageLoopCallback, messageInvoker);
_wsStream.ReadFrameAsync(completed);
}
private bool tryCreateUri(string uriString, out Uri result, out string message)
@ -1365,6 +1358,15 @@ namespace WebSocketSharp {
}
}
/// <summary>
/// Sends a text data asynchronously using the connection.
/// </summary>
/// <param name="data">
/// A <see cref="string"/> that contains the text data to be sent.
/// </param>
/// <param name="completed">
/// An <see cref="Action"/> delegate that contains the method(s) that is called when an asynchronous operation completes.
/// </param>
public void SendAsync(string data, Action completed)
{
if (data.IsNull())
@ -1377,6 +1379,15 @@ namespace WebSocketSharp {
sendAsync(Opcode.TEXT, buffer, completed);
}
/// <summary>
/// Sends a binary data asynchronously using the connection.
/// </summary>
/// <param name="data">
/// An array of <see cref="byte"/> that contains the binary data to be sent.
/// </param>
/// <param name="completed">
/// An <see cref="Action"/> delegate that contains the method(s) that is called when an asynchronous operation completes.
/// </param>
public void SendAsync(byte[] data, Action completed)
{
if (data.IsNull())
@ -1388,6 +1399,15 @@ namespace WebSocketSharp {
sendAsync(Opcode.BINARY, data, completed);
}
/// <summary>
/// Sends a binary data asynchronously using the connection.
/// </summary>
/// <param name="file">
/// A <see cref="FileInfo"/> that contains the binary data to be sent.
/// </param>
/// <param name="completed">
/// An <see cref="Action"/> delegate that contains the method(s) that is called when an asynchronous operation completes.
/// </param>
public void SendAsync(FileInfo file, Action completed)
{
if (file.IsNull())

@ -37,9 +37,9 @@ using System.Text;
using WebSocketSharp.Frame;
using WebSocketSharp.Net.Security;
namespace WebSocketSharp
{
public class WsStream : IDisposable
namespace WebSocketSharp {
internal class WsStream : IDisposable
{
#region Fields
@ -69,15 +69,16 @@ namespace WebSocketSharp
public bool DataAvailable {
get {
if (_innerStreamType == typeof(SslStream))
return ((SslStream)_innerStream).DataAvailable;
return ((NetworkStream)_innerStream).DataAvailable;
return _innerStreamType == typeof(SslStream)
? ((SslStream)_innerStream).DataAvailable
: ((NetworkStream)_innerStream).DataAvailable;
}
}
public bool IsSecure {
get { return _isSecure; }
get {
return _isSecure;
}
}
#endregion
@ -96,6 +97,50 @@ namespace WebSocketSharp
_forWrite = new object();
}
private int read(byte[] buffer, int offset, int size)
{
var readLen = _innerStream.Read(buffer, offset, size);
if (readLen < size)
{
var msg = String.Format("Data can not be read from {0}.", _innerStreamType);
throw new IOException(msg);
}
return readLen;
}
private int readByte()
{
return _innerStream.ReadByte();
}
private string[] readHandshake()
{
var buffer = new List<byte>();
while (true)
{
if (readByte().EqualsAndSaveTo('\r', buffer) &&
readByte().EqualsAndSaveTo('\n', buffer) &&
readByte().EqualsAndSaveTo('\r', buffer) &&
readByte().EqualsAndSaveTo('\n', buffer))
break;
}
return Encoding.UTF8.GetString(buffer.ToArray())
.Replace("\r\n", "\n").Replace("\n\n", "\n").TrimEnd('\n')
.Split('\n');
}
private void write(byte[] buffer, int offset, int count)
{
_innerStream.Write(buffer, offset, count);
}
private void writeByte(byte value)
{
_innerStream.WriteByte(value);
}
#endregion
#region Internal Methods
@ -145,10 +190,9 @@ namespace WebSocketSharp
var conn = context.Connection;
var stream = conn.Stream;
if (conn.IsSecure)
return new WsStream((SslStream)stream);
return new WsStream((NetworkStream)stream);
return conn.IsSecure
? new WsStream((SslStream)stream)
: new WsStream((NetworkStream)stream);
}
#endregion
@ -165,88 +209,74 @@ namespace WebSocketSharp
_innerStream.Dispose();
}
public int Read(byte[] buffer, int offset, int size)
public WsFrame ReadFrame()
{
lock (_forRead)
{
var readLen = _innerStream.Read(buffer, offset, size);
if (readLen < size)
try
{
var msg = String.Format("Data can not be read from {0}.", _innerStreamType);
throw new IOException(msg);
}
return readLen;
}
return WsFrame.Parse(_innerStream);
}
public int ReadByte()
{
lock (_forRead)
catch
{
return _innerStream.ReadByte();
return null;
}
}
}
public WsFrame ReadFrame()
{
lock (_forRead)
public void ReadFrameAsync(Action<WsFrame> completed)
{
return WsFrame.Parse(_innerStream);
}
WsFrame.ParseAsync(_innerStream, completed);
}
public string[] ReadHandshake()
{
lock (_forRead)
{
var buffer = new List<byte>();
while (true)
try
{
if (ReadByte().EqualsAndSaveTo('\r', buffer) &&
ReadByte().EqualsAndSaveTo('\n', buffer) &&
ReadByte().EqualsAndSaveTo('\r', buffer) &&
ReadByte().EqualsAndSaveTo('\n', buffer))
break;
}
return Encoding.UTF8.GetString(buffer.ToArray())
.Replace("\r\n", "\n").Replace("\n\n", "\n").TrimEnd('\n')
.Split('\n');
return readHandshake();
}
}
public void Write(byte[] buffer, int offset, int count)
{
lock (_forWrite)
catch
{
_innerStream.Write(buffer, offset, count);
}
return null;
}
public void WriteByte(byte value)
{
lock (_forWrite)
{
_innerStream.WriteByte(value);
}
}
public void WriteFrame(WsFrame frame)
public bool WriteFrame(WsFrame frame)
{
lock (_forWrite)
{
try
{
var buffer = frame.ToBytes();
_innerStream.Write(buffer, 0, buffer.Length);
write(buffer, 0, buffer.Length);
return true;
}
catch
{
return false;
}
}
}
public void WriteHandshake(Handshake handshake)
public bool WriteHandshake(Handshake handshake)
{
lock (_forWrite)
{
try
{
var buffer = handshake.ToBytes();
_innerStream.Write(buffer, 0, buffer.Length);
write(buffer, 0, buffer.Length);
return true;
}
catch
{
return false;
}
}
}

Loading…
Cancel
Save