From 02566951cab27ea9357667c25aede9f3da653be2 Mon Sep 17 00:00:00 2001 From: Matt Burnell Date: Fri, 20 Sep 2013 11:44:39 +1000 Subject: [PATCH 1/2] Fixed CPU / memory issue caused by infinite loop caused by ReadByte returning data always when connection is dropped. --- ImapClient.cs | 110 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 32 deletions(-) diff --git a/ImapClient.cs b/ImapClient.cs index 2fa285c..33a72f0 100644 --- a/ImapClient.cs +++ b/ImapClient.cs @@ -485,39 +485,85 @@ private string SendCommandGetResponse(string command, bool resolveLiterals = tru } return GetResponse(resolveLiterals); } - } + } + + /// + /// Waits for a response from the server. This method blocks + /// until a response has been received. + /// + /// Set to true to resolve possible literals + /// returned by the server (Refer to RFC 3501 Section 4.3 for details). + /// A response string from the server + private string GetResponse(bool resolveLiterals = true) + { + const int Newline = 10, CarriageReturn = 13; + const int ConnectionCheckThreshold = 100000; + try + { + using (var mem = new MemoryStream()) + { + var bytesRemainingUntilNextConnectionCheck = ConnectionCheckThreshold; + + lock (readLock) + { + while (true) + { + bytesRemainingUntilNextConnectionCheck--; + + if (bytesRemainingUntilNextConnectionCheck == 0) + { + CheckConnection(); + bytesRemainingUntilNextConnectionCheck = ConnectionCheckThreshold; + } + + byte b = (byte)stream.ReadByte(); + if (b == CarriageReturn) + continue; + if (b == Newline) + { + string s = Encoding.ASCII.GetString(mem.ToArray()); + if (resolveLiterals) + { + s = Regex.Replace(s, @"{(\d+)}$", m => + { + return "\"" + GetData(Convert.ToInt32(m.Groups[1].Value)) + + "\"" + GetResponse(false); + }); + } + ts.TraceInformation("S -> " + s); + return s; + } + else + mem.WriteByte(b); + } + } + } + } + catch (BadServerResponseException) + { + throw; + } + catch (Exception ex) + { + throw new BadServerResponseException("Error encountered during read; see inner exception for details", ex); + } + } + + /// + /// In the event that the SslStream is disconnected unceremoniously, it will continue to + /// return data from ReadByte() indefinitely. The below method should be called periodically + /// to check the health of the connection + /// + private void CheckConnection() + { + if (client.Client.Poll(0, SelectMode.SelectRead)) + { + var buffer = new byte[1]; + if (client.Client.Receive(buffer, SocketFlags.Peek) == 0) + throw new BadServerResponseException("Error encountered during read: not connected"); + } + } - /// - /// Waits for a response from the server. This method blocks - /// until a response has been received. - /// - /// Set to true to resolve possible literals - /// returned by the server (Refer to RFC 3501 Section 4.3 for details). - /// A response string from the server - private string GetResponse(bool resolveLiterals = true) { - const int Newline = 10, CarriageReturn = 13; - using (var mem = new MemoryStream()) { - lock (readLock) { - while (true) { - byte b = (byte)stream.ReadByte(); - if (b == CarriageReturn) - continue; - if (b == Newline) { - string s = Encoding.ASCII.GetString(mem.ToArray()); - if (resolveLiterals) { - s = Regex.Replace(s, @"{(\d+)}$", m => { - return "\"" + GetData(Convert.ToInt32(m.Groups[1].Value)) + - "\"" + GetResponse(false); - }); - } - ts.TraceInformation("S -> " + s); - return s; - } else - mem.WriteByte(b); - } - } - } - } /// /// Reads the specified amount of bytes from the server. This From 7f3c0bbb672ff4fa6544836274e029091e62cbe1 Mon Sep 17 00:00:00 2001 From: Matt Burnell Date: Mon, 23 Sep 2013 09:16:34 +1000 Subject: [PATCH 2/2] Made whitespace usage consistent with the rest of the file / project --- ImapClient.cs | 156 +++++++++++++++++++++++++------------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/ImapClient.cs b/ImapClient.cs index 33a72f0..b9b27bc 100644 --- a/ImapClient.cs +++ b/ImapClient.cs @@ -485,84 +485,84 @@ private string SendCommandGetResponse(string command, bool resolveLiterals = tru } return GetResponse(resolveLiterals); } - } - - /// - /// Waits for a response from the server. This method blocks - /// until a response has been received. - /// - /// Set to true to resolve possible literals - /// returned by the server (Refer to RFC 3501 Section 4.3 for details). - /// A response string from the server - private string GetResponse(bool resolveLiterals = true) - { - const int Newline = 10, CarriageReturn = 13; - const int ConnectionCheckThreshold = 100000; - try - { - using (var mem = new MemoryStream()) - { - var bytesRemainingUntilNextConnectionCheck = ConnectionCheckThreshold; - - lock (readLock) - { - while (true) - { - bytesRemainingUntilNextConnectionCheck--; - - if (bytesRemainingUntilNextConnectionCheck == 0) - { - CheckConnection(); - bytesRemainingUntilNextConnectionCheck = ConnectionCheckThreshold; - } - - byte b = (byte)stream.ReadByte(); - if (b == CarriageReturn) - continue; - if (b == Newline) - { - string s = Encoding.ASCII.GetString(mem.ToArray()); - if (resolveLiterals) - { - s = Regex.Replace(s, @"{(\d+)}$", m => - { - return "\"" + GetData(Convert.ToInt32(m.Groups[1].Value)) + - "\"" + GetResponse(false); - }); - } - ts.TraceInformation("S -> " + s); - return s; - } - else - mem.WriteByte(b); - } - } - } - } - catch (BadServerResponseException) - { - throw; - } - catch (Exception ex) - { - throw new BadServerResponseException("Error encountered during read; see inner exception for details", ex); - } - } - - /// - /// In the event that the SslStream is disconnected unceremoniously, it will continue to - /// return data from ReadByte() indefinitely. The below method should be called periodically - /// to check the health of the connection - /// - private void CheckConnection() - { - if (client.Client.Poll(0, SelectMode.SelectRead)) - { - var buffer = new byte[1]; - if (client.Client.Receive(buffer, SocketFlags.Peek) == 0) - throw new BadServerResponseException("Error encountered during read: not connected"); - } - } + } + + /// + /// Waits for a response from the server. This method blocks + /// until a response has been received. + /// + /// Set to true to resolve possible literals + /// returned by the server (Refer to RFC 3501 Section 4.3 for details). + /// A response string from the server + private string GetResponse(bool resolveLiterals = true) + { + const int Newline = 10, CarriageReturn = 13; + const int ConnectionCheckThreshold = 100000; + try + { + using (var mem = new MemoryStream()) + { + var bytesRemainingUntilNextConnectionCheck = ConnectionCheckThreshold; + + lock (readLock) + { + while (true) + { + bytesRemainingUntilNextConnectionCheck--; + + if (bytesRemainingUntilNextConnectionCheck == 0) + { + CheckConnection(); + bytesRemainingUntilNextConnectionCheck = ConnectionCheckThreshold; + } + + byte b = (byte)stream.ReadByte(); + if (b == CarriageReturn) + continue; + if (b == Newline) + { + string s = Encoding.ASCII.GetString(mem.ToArray()); + if (resolveLiterals) + { + s = Regex.Replace(s, @"{(\d+)}$", m => + { + return "\"" + GetData(Convert.ToInt32(m.Groups[1].Value)) + + "\"" + GetResponse(false); + }); + } + ts.TraceInformation("S -> " + s); + return s; + } + else + mem.WriteByte(b); + } + } + } + } + catch (BadServerResponseException) + { + throw; + } + catch (Exception ex) + { + throw new BadServerResponseException("Error encountered during read; see inner exception for details", ex); + } + } + + /// + /// In the event that the SslStream is disconnected unceremoniously, it will continue to + /// return data from ReadByte() indefinitely. The below method should be called periodically + /// to check the health of the connection + /// + private void CheckConnection() + { + if (client.Client.Poll(0, SelectMode.SelectRead)) + { + var buffer = new byte[1]; + if (client.Client.Receive(buffer, SocketFlags.Peek) == 0) + throw new BadServerResponseException("Error encountered during read: not connected"); + } + } ///