Modified reading the handshake

This commit is contained in:
sta 2014-01-16 17:16:40 +09:00
parent f48805a2d1
commit 413d5058f8
2 changed files with 86 additions and 68 deletions

View File

@ -4,8 +4,8 @@
* *
* The MIT License * The MIT License
* *
* Copyright (c) 2012-2013 sta.blockhead * Copyright (c) 2012-2014 sta.blockhead
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
@ -15,7 +15,7 @@
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -31,30 +31,41 @@ using System;
namespace WebSocketSharp namespace WebSocketSharp
{ {
/// <summary> /// <summary>
/// Represents the exception that occurred when attempting to perform an operation /// Represents the exception that occurred when attempting to perform an
/// on the WebSocket connection. /// operation on the WebSocket connection.
/// </summary> /// </summary>
public class WebSocketException : Exception public class WebSocketException : Exception
{ {
#region Internal Constructors #region Internal Constructors
internal WebSocketException () internal WebSocketException ()
: this (CloseStatusCode.ABNORMAL) : this (CloseStatusCode.ABNORMAL, null, null)
{ {
} }
internal WebSocketException (CloseStatusCode code) internal WebSocketException (CloseStatusCode code)
: this (code, null) : this (code, null, null)
{ {
} }
internal WebSocketException (string reason) internal WebSocketException (string reason)
: this (CloseStatusCode.ABNORMAL, reason) : this (CloseStatusCode.ABNORMAL, reason, null)
{
}
internal WebSocketException (string reason, Exception innerException)
: this (CloseStatusCode.ABNORMAL, reason, innerException)
{ {
} }
internal WebSocketException (CloseStatusCode code, string reason) internal WebSocketException (CloseStatusCode code, string reason)
: base (reason ?? code.GetMessage ()) : this (code, reason, null)
{
}
internal WebSocketException (
CloseStatusCode code, string reason, Exception innerException)
: base (reason ?? code.GetMessage (), innerException)
{ {
Code = code; Code = code;
} }
@ -64,10 +75,12 @@ namespace WebSocketSharp
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Gets the <see cref="CloseStatusCode"/> associated with the <see cref="WebSocketException"/>. /// Gets the <see cref="CloseStatusCode"/> associated with the
/// <see cref="WebSocketException"/>.
/// </summary> /// </summary>
/// <value> /// <value>
/// One of the <see cref="CloseStatusCode"/> values, indicates the causes of the <see cref="WebSocketException"/>. /// One of the <see cref="CloseStatusCode"/> values, indicates the cause of
/// the <see cref="WebSocketException"/>.
/// </value> /// </value>
public CloseStatusCode Code { public CloseStatusCode Code {
get; private set; get; private set;

View File

@ -42,7 +42,7 @@ namespace WebSocketSharp
{ {
#region Private Const Fields #region Private Const Fields
private const int _handshakeLimitLen = 8192; private const int _handshakeHeadersLimitLen = 8192;
private const int _handshakeTimeout = 90000; private const int _handshakeTimeout = 90000;
#endregion #endregion
@ -100,61 +100,33 @@ namespace WebSocketSharp
#region Private Methods #region Private Methods
private byte [] readHandshakeEntityBody (string length) private static byte [] readHandshakeEntityBody (Stream stream, string length)
{ {
var len = Int64.Parse (length); var len = Int64.Parse (length);
return len > 1024 return len > 1024
? _innerStream.ReadBytes (len, 1024) ? stream.ReadBytes (len, 1024)
: _innerStream.ReadBytes ((int) len); : stream.ReadBytes ((int) len);
} }
private string [] readHandshakeHeaders () private static string [] readHandshakeHeaders (Stream stream)
{ {
var read = false;
var exception = false;
var buffer = new List<byte> (); var buffer = new List<byte> ();
Action<int> add = i => buffer.Add ((byte) i); Action<int> add = i => buffer.Add ((byte) i);
var timeout = false; var read = false;
var timer = new Timer ( while (buffer.Count < _handshakeHeadersLimitLen) {
state => { if (stream.ReadByte ().EqualsWith ('\r', add) &&
timeout = true; stream.ReadByte ().EqualsWith ('\n', add) &&
_innerStream.Close (); stream.ReadByte ().EqualsWith ('\r', add) &&
}, stream.ReadByte ().EqualsWith ('\n', add)) {
null, read = true;
_handshakeTimeout, break;
-1);
try {
while (buffer.Count < _handshakeLimitLen) {
if (_innerStream.ReadByte ().EqualsWith ('\r', add) &&
_innerStream.ReadByte ().EqualsWith ('\n', add) &&
_innerStream.ReadByte ().EqualsWith ('\r', add) &&
_innerStream.ReadByte ().EqualsWith ('\n', add)) {
read = true;
break;
}
} }
} }
catch {
exception = true;
}
finally {
timer.Change (-1, -1);
timer.Dispose ();
}
var reason = timeout if (!read)
? "A timeout has occurred while receiving a handshake." throw new WebSocketException (
: exception "The header part of a handshake is greater than the limit length.");
? "An exception has occurred while receiving a handshake."
: !read
? "A handshake length is greater than the limit length."
: null;
if (reason != null)
throw new WebSocketException (reason);
return Encoding.UTF8.GetString (buffer.ToArray ()) return Encoding.UTF8.GetString (buffer.ToArray ())
.Replace ("\r\n", "\n") .Replace ("\r\n", "\n")
@ -208,6 +180,48 @@ namespace WebSocketSharp
return new WsStream (conn.Stream, conn.IsSecure); return new WsStream (conn.Stream, conn.IsSecure);
} }
internal T ReadHandshake<T> (Func<string [], T> parser)
where T : HandshakeBase
{
var timeout = false;
var timer = new Timer (
state => {
timeout = true;
_innerStream.Close ();
},
null,
_handshakeTimeout,
-1);
T handshake = null;
Exception exception = null;
try {
handshake = parser (readHandshakeHeaders (_innerStream));
var contentLen = handshake.Headers ["Content-Length"];
if (contentLen != null && contentLen.Length > 0)
handshake.EntityBodyData = readHandshakeEntityBody (
_innerStream, contentLen);
}
catch (Exception ex) {
exception = ex;
}
finally {
timer.Change (-1, -1);
timer.Dispose ();
}
var reason = timeout
? "A timeout has occurred while receiving a handshake."
: exception != null
? "An exception has occurred while receiving a handshake."
: null;
if (reason != null)
throw new WebSocketException (reason, exception);
return handshake;
}
internal bool Write (byte [] data) internal bool Write (byte [] data)
{ {
lock (_forWrite) { lock (_forWrite) {
@ -240,29 +254,20 @@ namespace WebSocketSharp
return WsFrame.Parse (_innerStream, true); return WsFrame.Parse (_innerStream, true);
} }
public void ReadFrameAsync (Action<WsFrame> completed, Action<Exception> error) public void ReadFrameAsync (
Action<WsFrame> completed, Action<Exception> error)
{ {
WsFrame.ParseAsync (_innerStream, true, completed, error); WsFrame.ParseAsync (_innerStream, true, completed, error);
} }
public HandshakeRequest ReadHandshakeRequest () public HandshakeRequest ReadHandshakeRequest ()
{ {
var req = HandshakeRequest.Parse (readHandshakeHeaders ()); return ReadHandshake<HandshakeRequest> (HandshakeRequest.Parse);
var contentLen = req.Headers ["Content-Length"];
if (contentLen != null && contentLen.Length > 0)
req.EntityBodyData = readHandshakeEntityBody (contentLen);
return req;
} }
public HandshakeResponse ReadHandshakeResponse () public HandshakeResponse ReadHandshakeResponse ()
{ {
var res = HandshakeResponse.Parse (readHandshakeHeaders ()); return ReadHandshake<HandshakeResponse> (HandshakeResponse.Parse);
var contentLen = res.Headers ["Content-Length"];
if (contentLen != null && contentLen.Length > 0)
res.EntityBodyData = readHandshakeEntityBody (contentLen);
return res;
} }
public bool WriteFrame (WsFrame frame) public bool WriteFrame (WsFrame frame)