/// <summary> /// Sends the handshake asynchronous. /// </summary> /// <param name="handshake">The handshake.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public async Task <WebSocketResponseHandshake> SendHandshakeAsync(WebSocketRequestHandshake handshake, CancellationToken cancellationToken) { var oldState = Interlocked.CompareExchange(ref _state, WebSocketState.Opening, WebSocketState.Connected); if (oldState != WebSocketState.Connected) { throw new InvalidOperationException(ErrorMessages.InvalidState + _state); } var data = handshake.ToString(); await this.SendAsync(data, Encoding.UTF8, cancellationToken); var responseHeaders = new List <string>(); var line = await _tcp.ReadLineAsync(cancellationToken); while (!String.IsNullOrEmpty(line)) { responseHeaders.Add(line); line = await _tcp.ReadLineAsync(cancellationToken); } var response = WebSocketResponseHandshake.Parse(responseHeaders); if (response.StatusCode != HttpStatusCode.SwitchingProtocols) { var versions = response.SecWebSocketVersion; if (versions != null && !versions.Intersect(Consts.SupportedClientVersions).Any()) { throw new WebSocketException(WebSocketErrorCode.HandshakeVersionNotSupported); } throw new WebSocketException(WebSocketErrorCode.HandshakeInvalidStatusCode); } var challenge = Encoding.UTF8.GetBytes(handshake.SecWebSocketKey + Consts.ServerGuid); var hash = Sha1Digest.ComputeHash(challenge); var calculatedAccept = Convert.ToBase64String(hash); if (response.SecWebSocketAccept != calculatedAccept) { throw new WebSocketException(WebSocketErrorCode.HandshakeInvalidSecWebSocketAccept); } response.RequestMessage = handshake; Interlocked.Exchange(ref _state, WebSocketState.Open); return(response); }
public static WebSocketResponseHandshake Parse(IList <string> responseLines) { if (responseLines == null || responseLines.Count < 1) { throw new ArgumentException(ErrorMessages.NoHeaderLines, "responseLines"); } var responseLine = responseLines[0].Split(' '); if (responseLine.Length < 3) { throw new ArgumentException(ErrorMessages.InvalidResponseLine + responseLines[0], "responseLines"); } var response = new WebSocketResponseHandshake { StatusCode = (HttpStatusCode)Convert.ToInt32(responseLine[1]), ReasonPhrase = string.Join(" ", responseLine.Skip(2)), Version = new Version(responseLine[0].Substring(5)), // "HTTP/x.x" }; foreach (var line in responseLines.Skip(1)) { if (string.IsNullOrEmpty(line)) { break; } var pos = line.IndexOf(':'); if (pos < 0) { continue; } var key = line.Substring(0, pos).Trim().ToLowerInvariant(); var value = line.Substring(pos + 1).Trim(); var values = value.Split(',').Select(v => v.Trim()).Where(v => v.Length > 0); if (key == "date") { response.Headers.Add(key, value); } else if (key.StartsWith("content-") || key == "expires" || key == "last-modified") { if (response.Content == null) { response.Content = new CustomHttpContent(); } if (key == "expires") { int offset; if (int.TryParse(value, out offset)) // "0" or "-1" { response.Content.Headers.Add(key, DateTime.UtcNow.AddSeconds(offset).ToString("R")); } else { response.Content.Headers.Add(key, value); } } else { response.Content.Headers.Add(key, values); } } else { try { response.Headers.Add(key, values); } catch { try { response.Headers.Add(key, value); } catch (Exception ex) { response.LogWarning("Failed to add header '{0}': {1}", key, ex); } } } } return(response); }