/// <summary>
      /// Attempt to parse the given text into an HTTPClientHandshake object
      /// </summary>
      /// <returns><c>true</c>, if parse was successful <c>false</c> otherwise.</returns>
      /// <param name="text">The complete HTTP header to be parsed</param>
      /// <param name="result">The parsed header as an HTTPClientHandshake object</param>
      /// <param name="error">Any error message from parsing... just for you!</param>
      public static bool TryParse(string text, out HTTPClientHandshake result, out string error)
      {
         //Set some initial dingles
         error = "";
         result = new HTTPClientHandshake();

         List<string> lines;
         List<string> lineArguments;
         List<string> subArguments;

         //First, replace the garbage.
         text = text.Replace("\r\n", "\n");

         //Now actually get the lines, dawg.
         lines = text.Split("\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
         lines = lines.Select(x => x.Trim()).ToList();

         //Man, don't be givin' us no flack
         if (lines.Count < 1)
         {
            error = "Handshake was empty!";
            return false;
         }
         
         //First line MUST be the GET thing. Break by space.
         lineArguments = Regex.Split(lines[0], @"\s+").ToList();

         //It's a bad request or something.
         if (lineArguments.Count != 3 || lineArguments[0].ToUpperInvariant() != "GET")
         {
            error = "HTTP Request was poorly formatted! (1st argument)";
            return false;
         }

         //Retrieve the service
         subArguments = lineArguments[1].Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();

         if (subArguments.Count < 1)
         {
            error = "HTTP Request was poorly formatted! (2nd argument)";
            return false;
         }

         result.Service = subArguments[subArguments.Count - 1];

         //Retrieve the HTTP version
         subArguments = lineArguments[2].Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();

         if (subArguments.Count != 2 || subArguments[0].ToUpperInvariant() != "HTTP")
         {
            error = "HTTP Request was poorly formatted! (3rd argument)";
            return false;
         }
         else if ((new Version(subArguments[1])).CompareTo(new Version(ExpectedHTTPVersion)) < 0)
         {
            error = "HTTP version too low! (expected " + ExpectedHTTPVersion + "+)";
            return false;
         }

         result.HTTPVersion = subArguments[1].Trim();

         Dictionary<string, bool> completes = ExpectedFields.ToDictionary(x => x, y => false);

         //OK, NOW we can start retrieving those fields dawg. Some are required.
         for (int i = 1; i < lines.Count; i++)
         {
            Match match = Regex.Match(lines[i], @"^([a-zA-Z\-]+)\s*:\s*(.+)$");

            //Ignore bad lines
            if (!match.Success)
               continue;

            string key = match.Groups[1].Value.Trim();
            string field = match.Groups[2].Value.Trim();
            List<string> values = WebSocketHelper.Explode(field);

            //Check expected field values for correctness
            if (key == "Upgrade" && field.ToLowerInvariant() != "websocket")
            {
               error = "Bad Upgrade field!";
               return false;
            }
            else if (key == "Connection" && !values.Contains("Upgrade"))
            {
               error = "Bad Connection field! (" + field + ")";
               return false;
            }
            else if (key == "Sec-WebSocket-Version" && field != ExpectedWebSocketVersion)
            {
               error = "Bad Sec-WebSocket-Version! (expected " + ExpectedWebSocketVersion + ")";
               return false;
            }
            else if (key == "Host")
            {
               result.Host = field;
            }
            else if (key == "Sec-WebSocket-Key")
            {
               result.Key = field;
            }
            else if (key == "Origin")
            {
               result.Origin = field;
            }
            else if (key == "Sec-WebSocket-Protocol")
            {
               result.Protocols = values;
            }
            else if (key == "Sec-WebSocket-Extensions")
            {
               result.Extensions = values;
            }

            //If this is an expected field, say that we saw it (we would've quit if it was bad)
            if (completes.ContainsKey(key))
               completes[key] = true;
         }

         return true;
      }
      /// <summary>
      /// Read and properly parse the message for the HTTP handshake portion of
      /// a WebSocket connection.
      /// </summary>
      /// <returns>The read handshake.</returns>
      public async Task<Tuple<DataStatus, HTTPClientHandshake, string>> ReadHandshakeAsync()
      {
         //You've already done the handshake, you idiot.
         if (HandShakeComplete)
            return Tuple.Create(DataStatus.Complete, parsedHandshake, "");

         string handshake = "";
         int handshakeEnd = 0;
         string error = "";
         HTTPClientHandshake result = new HTTPClientHandshake();

         //Keep repeating until we have the whole handshake. It's OK if the stream stops in the middle
         //of the operation, because we'll just return the proper data status.
         do
         {
            //Pull a chunk of data (as much as we can) from the stream and store it in our internal buffer.
            DataStatus readStatus = await GenericReadAsync();

            //If there was an error (anything other than "completion"), return the error.
            if (readStatus != DataStatus.Complete)
               return Tuple.Create(readStatus, result, "");

            //Now let's see if we read the whole header by searching for the header ending symbol.
            handshake = System.Text.Encoding.ASCII.GetString(messageBuffer, 0, messageBufferSize);
            handshakeEnd = handshake.IndexOf("\r\n\r\n");

         } while(handshakeEnd < 0);

         //We read the whole header, now it's time to parse it.
         if (HTTPClientHandshake.TryParse(handshake, out result, out error))
         {
            //Push the data in the buffer back. We may have read a bit of the new data.
            MessageBufferPop(handshakeEnd + 4);
//            messageBuffer.TruncateBeginning(handshakeEnd + 4, messageBufferSize);
//            messageBufferSize -= (handshakeEnd + 4);

            parsedHandshake = result;
            return Tuple.Create(DataStatus.Complete, result, error);
         }
         else
         {
            return Tuple.Create(DataStatus.DataFormatError, result, error);
         }
      }
 public static HTTPServerHandshake GetResponseForClientHandshake(HTTPClientHandshake handshake)
 {
    HTTPServerHandshake response = new HTTPServerHandshake();
    response.AcceptedProtocols = new List<string>(handshake.Protocols);
    response.AcceptedExtensions = new List<string>(handshake.Extensions);
    response.AcceptKey = GenerateAcceptKey(handshake.Key);
    response.HTTPVersion = handshake.HTTPVersion;
    response.Status = "101 Switching Protocols";
    return response;
 }
      /// <summary>
      /// Attempts to pull all the data and properly parse it for the HTTP handshake portion of
      /// a WebSocket connection. Performs minimal blocking.
      /// </summary>
      /// <returns>The read handshake.</returns>
      public DataStatus TryReadHandshake(out HTTPClientHandshake result, out string error)
      {
         result = new HTTPClientHandshake();
         error = "";

         //You've already done the handshake, you idiot.
         if (HandShakeComplete)
         {
            result = parsedHandshake;
            return DataStatus.Complete;
         }

         //Pull a chunk of data (as much as we can) from the stream and store it in our internal buffer.
         DataStatus readStatus = GenericRead();

         //If there was an error (anything other than "completion"), return the error.
         if (readStatus != DataStatus.Complete)
            return readStatus;
         
         //Now let's see if we read the whole header by searching for the header ending symbol.
         string handshake = System.Text.Encoding.ASCII.GetString(messageBuffer, 0, messageBufferSize);
         int handshakeEnd = handshake.IndexOf("\r\n\r\n");

         //We read the whole header, now it's time to parse it.
         if(handshakeEnd >= 0)
         {
            if(HTTPClientHandshake.TryParse(handshake, out result, out error))
            {
               //Push the data in the buffer back. We may have read a bit of the new data.
               MessageBufferPop(handshakeEnd + 4);
//               messageBuffer.TruncateBeginning(handshakeEnd + 4, messageBufferSize);
//               messageBufferSize -= (handshakeEnd + 4);

               parsedHandshake = result;
               return DataStatus.Complete;
            }
            else
            {
               return DataStatus.DataFormatError;
            }
         }
         else
         {
            //If we didn't read the whole header, we're still basically waiting on data.
            return DataStatus.WaitingOnData;
         }
      }