private void ProcessInit() { // continue initializing through the html channel initSource.Process(); // stop processing if something went wrong if (initSource.State == WebChannelState.Closed || initSource.State == WebChannelState.InitFailed) { State = WebChannelState.InitFailed; initSource = null; } // wait for the first header to arrive if (initSource.AvailableMessageNo > 0) { HttpHeader header = initSource.EmitMessage().Header; string headerKey = string.Empty; bool headerValid = false; bool canRecover = false; bool denySubprotocol = false; bool denyExtension = false; do { // check the header for errors if (header.Method != HttpMethod.Get) { break; } // TODO optional origin string connection; if (!header.Fields.TryGetValue("Connection", out connection)) { break; } if (!connection.ToLower().Contains("upgrade")) { break; } string upgrade; if (!header.Fields.TryGetValue("Upgrade", out upgrade)) { break; } if (!upgrade.ToLower().Contains("websocket")) { break; } if (!header.Fields.TryGetValue("Sec-WebSocket-Key", out headerKey)) { break; } string sVersion; if (header.Fields.TryGetValue("Sec-WebSocket-Version", out sVersion)) { // TODO improve detection bool acceptVersion = false; if (sVersion.Contains("13")) { acceptVersion = true; } if (!acceptVersion) { headerValid = false; canRecover = true; HttpHeader response = new HttpHeader(header.Version, HttpStatusCode.UpgradeRequired); response.Fields.Add("Sec-WebSocket-Version", "13"); initSource.SendMessage(new HttpMessage(response)); break; } } // this implementation does not support subprotocols or extensions // send null fields if these exist in the request if (header.Fields.ContainsKey("Subprotocol")) { denySubprotocol = true; } if (header.Fields.ContainsKey("Extension")) { denyExtension = true; } // TODO optional handle resource or send 404 not found headerValid = true; } while (false); if (!headerValid) { if (!canRecover) { // set the connection on ice if the setup failed State = WebChannelState.InitFailed; initSource.Close(false); initSource = null; } } else { // send an appropriate response header HttpHeader response = new HttpHeader(HttpVersion.Version11, HttpStatusCode.SwitchingProtocols); response.Fields.Add("Upgrade", "websocket"); response.Fields.Add("Connection", "Upgrade"); byte[] byteKey = Encoding.UTF8.GetBytes(headerKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); byte[] sha1Key = System.Security.Cryptography.SHA1.Create().ComputeHash(byteKey); string base64Key = Convert.ToBase64String(sha1Key); response.Fields.Add("Sec-WebSocket-Accept", base64Key); if (denySubprotocol) { response.Fields.Add("Subprotocol", "null"); } if (denyExtension) { response.Fields.Add("Extension", "null"); } initSource.SendMessage(new HttpMessage(response)); // since the upgrade is done no more http communication is necessary // ACK has to be sent first so initSource can not contain any websocket messages at this point initSource.Flush(); initSource.Close(false); initSource = null; // the handshake has been completed // messages can now be sent State = WebChannelState.Open; // continue with sending all pending messages foreach (WebSocketMessage send in toSend) { byte[] encoded = EncodeMessage(send); messageSource.SendMessage(encoded); } toSend.Clear(); } } }
public static HttpHeader Parse(string text) { var result = new HttpHeader(); // TODO make compatible with RESPONSE messages string[] split = text.Split("\r\n", StringSplitOptions.RemoveEmptyEntries); if (split.Length == 0) { throw new FormatException(text); } // read header string head = split[0]; string[] sHead = head.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (sHead.Length < 3) { throw new FormatException(head); } // read method try { result.Method = ParseHttpMethod(sHead[0]); } catch (FormatException) { throw new FormatException(sHead[0]); } // read resource result.Resource = sHead[1]; // read protocol if (sHead[2].Length < ("HTTP/".Length + "1.1".Length)) { throw new FormatException(sHead[2]); } if (!sHead[2].StartsWith("HTTP/", StringComparison.InvariantCulture)) { throw new FormatException(sHead[2]); } string version = sHead[2].Substring("HTTP/".Length, 3); try { result.Version = Version.Parse(version); } catch (FormatException) { throw new FormatException(sHead[2]); } // read fields for (int s = 1; s < split.Length; s++) { string field = split[s]; int appearance = field.IndexOf(':'); if (appearance < 0) { throw new FormatException(split[s]); } //if (appearance >= field.Length - 1) throw new FormatException(split[s]); string key = field.Substring(0, appearance).Trim(); string value = string.Empty; if (appearance < field.Length - 1) { value = field.Substring(appearance + 1).Trim(); } result.Fields.Add(key, value); } return(result); }